]> git.lizzy.rs Git - rust.git/commitdiff
Merge branch 'master' of https://github.com/rust-lang/rust into gen
authorJohn Kåre Alsaker <john.kare.alsaker@gmail.com>
Sat, 12 Aug 2017 05:11:19 +0000 (07:11 +0200)
committerJohn Kåre Alsaker <john.kare.alsaker@gmail.com>
Sat, 12 Aug 2017 05:11:19 +0000 (07:11 +0200)
# Conflicts:
# src/librustc_mir/build/scope.rs

159 files changed:
src/doc/unstable-book/src/language-features/generators.md [new file with mode: 0644]
src/liballoc/boxed.rs
src/liballoc/lib.rs
src/libcore/ops/generator.rs [new file with mode: 0644]
src/libcore/ops/mod.rs
src/librustc/cfg/construct.rs
src/librustc/diagnostics.rs
src/librustc/hir/intravisit.rs
src/librustc/hir/lowering.rs
src/librustc/hir/map/blocks.rs
src/librustc/hir/map/mod.rs
src/librustc/hir/mod.rs
src/librustc/hir/print.rs
src/librustc/ich/impls_hir.rs
src/librustc/ich/impls_mir.rs
src/librustc/ich/impls_ty.rs
src/librustc/infer/freshen.rs
src/librustc/infer/mod.rs
src/librustc/middle/expr_use_visitor.rs
src/librustc/middle/lang_items.rs
src/librustc/middle/liveness.rs
src/librustc/middle/mem_categorization.rs
src/librustc/middle/region.rs
src/librustc/mir/mod.rs
src/librustc/mir/tcx.rs
src/librustc/mir/transform.rs
src/librustc/mir/visit.rs
src/librustc/traits/coherence.rs
src/librustc/traits/error_reporting.rs
src/librustc/traits/mod.rs
src/librustc/traits/project.rs
src/librustc/traits/select.rs
src/librustc/traits/structural_impls.rs
src/librustc/traits/util.rs
src/librustc/ty/context.rs
src/librustc/ty/error.rs
src/librustc/ty/fast_reject.rs
src/librustc/ty/flags.rs
src/librustc/ty/item_path.rs
src/librustc/ty/layout.rs
src/librustc/ty/maps.rs
src/librustc/ty/mod.rs
src/librustc/ty/outlives.rs
src/librustc/ty/relate.rs
src/librustc/ty/structural_impls.rs
src/librustc/ty/sty.rs
src/librustc/ty/util.rs
src/librustc/ty/walk.rs
src/librustc/ty/wf.rs
src/librustc/util/ppaux.rs
src/librustc_borrowck/borrowck/gather_loans/mod.rs
src/librustc_borrowck/borrowck/mod.rs
src/librustc_borrowck/diagnostics.rs
src/librustc_data_structures/indexed_set.rs
src/librustc_data_structures/indexed_vec.rs
src/librustc_data_structures/stable_hasher.rs
src/librustc_driver/driver.rs
src/librustc_lint/types.rs
src/librustc_metadata/cstore_impl.rs
src/librustc_metadata/decoder.rs
src/librustc_metadata/encoder.rs
src/librustc_metadata/schema.rs
src/librustc_mir/build/expr/as_lvalue.rs
src/librustc_mir/build/expr/as_rvalue.rs
src/librustc_mir/build/expr/category.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/matches/mod.rs
src/librustc_mir/build/mod.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/dataflow/mod.rs
src/librustc_mir/dataflow/move_paths/mod.rs
src/librustc_mir/hair/cx/expr.rs
src/librustc_mir/hair/cx/mod.rs
src/librustc_mir/hair/mod.rs
src/librustc_mir/shim.rs
src/librustc_mir/transform/copy_prop.rs
src/librustc_mir/transform/elaborate_drops.rs
src/librustc_mir/transform/generator.rs [new file with mode: 0644]
src/librustc_mir/transform/inline.rs
src/librustc_mir/transform/mod.rs
src/librustc_mir/transform/no_landing_pads.rs
src/librustc_mir/transform/promote_consts.rs
src/librustc_mir/transform/qualify_consts.rs
src/librustc_mir/transform/type_check.rs
src/librustc_mir/util/elaborate_drops.rs
src/librustc_mir/util/liveness.rs [new file with mode: 0644]
src/librustc_mir/util/mod.rs
src/librustc_mir/util/patch.rs
src/librustc_mir/util/pretty.rs
src/librustc_passes/consts.rs
src/librustc_passes/loops.rs
src/librustc_passes/mir_stats.rs
src/librustc_trans/adt.rs
src/librustc_trans/collector.rs
src/librustc_trans/common.rs
src/librustc_trans/debuginfo/metadata.rs
src/librustc_trans/debuginfo/type_names.rs
src/librustc_trans/mir/analyze.rs
src/librustc_trans/mir/block.rs
src/librustc_trans/mir/constant.rs
src/librustc_trans/mir/mod.rs
src/librustc_trans/monomorphize.rs
src/librustc_trans/trans_item.rs
src/librustc_trans/type_of.rs
src/librustc_typeck/check/closure.rs
src/librustc_typeck/check/generator_interior.rs [new file with mode: 0644]
src/librustc_typeck/check/method/probe.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/regionck.rs
src/librustc_typeck/check/upvar.rs
src/librustc_typeck/check/writeback.rs
src/librustc_typeck/collect.rs
src/librustc_typeck/diagnostics.rs
src/librustc_typeck/variance/constraints.rs
src/librustdoc/clean/mod.rs
src/libsyntax/ast.rs
src/libsyntax/feature_gate.rs
src/libsyntax/fold.rs
src/libsyntax/parse/parser.rs
src/libsyntax/parse/token.rs
src/libsyntax/print/pprust.rs
src/libsyntax/visit.rs
src/test/compile-fail/feature-gate-generators.rs [new file with mode: 0644]
src/test/run-pass/generator/auxiliary/xcrate.rs [new file with mode: 0644]
src/test/run-pass/generator/conditional-drop.rs [new file with mode: 0644]
src/test/run-pass/generator/control-flow.rs [new file with mode: 0644]
src/test/run-pass/generator/drop-env.rs [new file with mode: 0644]
src/test/run-pass/generator/iterator-count.rs [new file with mode: 0644]
src/test/run-pass/generator/panic-drops.rs [new file with mode: 0644]
src/test/run-pass/generator/panic-safe.rs [new file with mode: 0644]
src/test/run-pass/generator/resume-after-return.rs [new file with mode: 0644]
src/test/run-pass/generator/smoke.rs [new file with mode: 0644]
src/test/run-pass/generator/xcrate.rs [new file with mode: 0644]
src/test/run-pass/generator/yield-subtype.rs [new file with mode: 0644]
src/test/ui/generator/borrowing.rs [new file with mode: 0644]
src/test/ui/generator/borrowing.stderr [new file with mode: 0644]
src/test/ui/generator/no-arguments-on-generators.rs [new file with mode: 0644]
src/test/ui/generator/no-arguments-on-generators.stderr [new file with mode: 0644]
src/test/ui/generator/not-send-sync.rs [new file with mode: 0644]
src/test/ui/generator/not-send-sync.stderr [new file with mode: 0644]
src/test/ui/generator/ref-escapes-but-not-over-yield.rs [new file with mode: 0644]
src/test/ui/generator/ref-escapes-but-not-over-yield.stderr [new file with mode: 0644]
src/test/ui/generator/yield-in-args-rev.rs [new file with mode: 0644]
src/test/ui/generator/yield-in-args-rev.stderr [new file with mode: 0644]
src/test/ui/generator/yield-in-args.rs [new file with mode: 0644]
src/test/ui/generator/yield-in-args.stderr [new file with mode: 0644]
src/test/ui/generator/yield-in-const.rs [new file with mode: 0644]
src/test/ui/generator/yield-in-const.stderr [new file with mode: 0644]
src/test/ui/generator/yield-in-function.rs [new file with mode: 0644]
src/test/ui/generator/yield-in-function.stderr [new file with mode: 0644]
src/test/ui/generator/yield-in-static.rs [new file with mode: 0644]
src/test/ui/generator/yield-in-static.stderr [new file with mode: 0644]
src/test/ui/generator/yield-while-iterating.rs [new file with mode: 0644]
src/test/ui/generator/yield-while-iterating.stderr [new file with mode: 0644]
src/test/ui/generator/yield-while-local-borrowed.rs [new file with mode: 0644]
src/test/ui/generator/yield-while-local-borrowed.stderr [new file with mode: 0644]
src/test/ui/generator/yield-while-ref-reborrowed.rs [new file with mode: 0644]
src/test/ui/generator/yield-while-ref-reborrowed.stderr [new file with mode: 0644]
src/test/ui/resolve/token-error-correct.stderr

diff --git a/src/doc/unstable-book/src/language-features/generators.md b/src/doc/unstable-book/src/language-features/generators.md
new file mode 100644 (file)
index 0000000..3b7b77e
--- /dev/null
@@ -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.
index 6318d22059f96c09339337a9f32dd23453b1a96c..17dc3d03aae0ebc7afcbeb7c7dd5a3a8a524fb4b 100644 (file)
@@ -66,7 +66,7 @@
 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;
@@ -784,3 +784,14 @@ fn as_mut(&mut self) -> &mut 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()
+    }
+}
index 227fcfabcf11de25056fbdf7918a7ae004853dcb..6c852a13867a81d818cfebdff505d83a9ff016e7 100644 (file)
@@ -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)]
diff --git a/src/libcore/ops/generator.rs b/src/libcore/ops/generator.rs
new file mode 100644 (file)
index 0000000..798c182
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// The result of a generator resumption.
+///
+/// This enum is returned from the `Generator::resume` method and indicates the
+/// possible return values of a generator. Currently this corresponds to either
+/// a suspension point (`Yielded`) or a termination point (`Complete`).
+#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
+#[cfg_attr(not(stage0), lang = "generator_state")]
+#[unstable(feature = "generator_trait", issue = "43122")]
+pub enum GeneratorState<Y, R> {
+    /// The generator suspended with a value.
+    ///
+    /// This state indicates that a generator has been suspended, and typically
+    /// corresponds to a `yield` statement. The value provided in this variant
+    /// corresponds to the expression passed to `yield` and allows generators to
+    /// provide a value each time they yield.
+    Yielded(Y),
+
+    /// The generator completed with a return value.
+    ///
+    /// This state indicates that a generator has finished execution with the
+    /// provided value. Once a generator has returned `Complete` it is
+    /// considered a programmer error to call `resume` again.
+    Complete(R),
+}
+
+/// The trait implemented by builtin generator types.
+///
+/// Generators, also commonly referred to as coroutines, are currently an
+/// experimental language feature in Rust. Added in [RFC 2033] generators are
+/// currently intended to primarily provide a building block for async/await
+/// syntax but will likely extend to also providing an ergonomic definition for
+/// iterators and other primitives.
+///
+/// The syntax and semantics for generators is unstable and will require a
+/// further RFC for stabilization. At this time, though, the syntax is
+/// closure-like:
+///
+/// ```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 return from resume"),
+///     }
+///     match generator.resume() {
+///         GeneratorState::Complete("foo") => {}
+///         _ => panic!("unexpected return from resume"),
+///     }
+/// }
+/// ```
+///
+/// More documentation of generators can be found in the unstable book.
+///
+/// [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
+#[cfg_attr(not(stage0), lang = "generator")]
+#[unstable(feature = "generator_trait", issue = "43122")]
+#[fundamental]
+pub trait Generator {
+    /// The type of value this generator yields.
+    ///
+    /// This associated type corresponds to the `yield` expression and the
+    /// values which are allowed to be returned each time a generator yields.
+    /// For example an iterator-as-a-generator would likely have this type as
+    /// `T`, the type being iterated over.
+    type Yield;
+
+    /// The type of value this generator returns.
+    ///
+    /// This corresponds to the type returned from a generator either with a
+    /// `return` statement or implicitly as the last expression of a generator
+    /// literal. For example futures would use this as `Result<T, E>` as it
+    /// represents a completed future.
+    type Return;
+
+    /// Resumes the execution of this generator.
+    ///
+    /// This function will resume execution of the generator or start execution
+    /// if it hasn't already. This call will return back into the generator's
+    /// last suspension point, resuming execution from the latest `yield`. The
+    /// generator will continue executing until it either yields or returns, at
+    /// which point this function will return.
+    ///
+    /// # Return value
+    ///
+    /// The `GeneratorState` enum returned from this function indicates what
+    /// state the generator is in upon returning. If the `Yielded` variant is
+    /// returned then the generator has reached a suspension point and a value
+    /// has been yielded out. Generators in this state are available for
+    /// resumption at a later point.
+    ///
+    /// If `Complete` is returned then the generator has completely finished
+    /// with the value provided. It is invalid for the generator to be resumed
+    /// again.
+    ///
+    /// # Panics
+    ///
+    /// This function may panic if it is called after the `Complete` variant has
+    /// been returned previously. While generator literals in the language are
+    /// guaranteed to panic on resuming after `Complete`, this is not guaranteed
+    /// for all implementations of the `Generator` trait.
+    fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
+}
+
+#[unstable(feature = "generator_trait", issue = "43122")]
+impl<'a, T> Generator for &'a mut T
+    where T: Generator + ?Sized
+{
+    type Yield = T::Yield;
+    type Return = T::Return;
+    fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
+        (**self).resume()
+    }
+}
index a78f4fe28a6b411078325ada32db8f4381561c3c..767b22e851eec3cb87dcca490574b350ff656d7c 100644 (file)
 mod deref;
 mod drop;
 mod function;
+mod generator;
 mod index;
 mod place;
 mod range;
 #[unstable(feature = "try_trait", issue = "42327")]
 pub use self::try::Try;
 
+#[unstable(feature = "generator_trait", issue = "43122")]
+pub use self::generator::{Generator, GeneratorState};
+
 #[unstable(feature = "placement_new_protocol", issue = "27779")]
 pub use self::place::{Place, Placer, InPlace, Boxed, BoxPlace};
 
index fa6b78045ffad75bfa0169f5b1ebb0987c077e2d..e7e2c84fc4efa3c99ffb85e50cc5a4674e8dbcb5 100644 (file)
@@ -389,6 +389,7 @@ fn expr(&mut self, expr: &hir::Expr, pred: CFGIndex) -> CFGIndex {
             hir::ExprUnary(_, ref e) |
             hir::ExprField(ref e, _) |
             hir::ExprTupField(ref e, _) |
+            hir::ExprYield(ref e) |
             hir::ExprRepeat(ref e, _) => {
                 self.straightline(expr, pred, Some(&**e).into_iter())
             }
index 522c1531c593804228a9bbad830df4d7756ef6d0..23616b4bc3851d2403a3ba8347e17235483fc1e3 100644 (file)
@@ -2026,4 +2026,5 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
     E0495, // cannot infer an appropriate lifetime due to conflicting requirements
     E0566, // conflicting representation hints
     E0623, // lifetime mismatch where both parameters are anonymous regions
+    E0625, // generators cannot have explicit arguments
 }
index 57198d8ca0b77433ea8a265c8381ee68f51670a0..43496540c1177f8f8f76b3c8c4cd77a058b51e1c 100644 (file)
@@ -978,7 +978,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             visitor.visit_expr(subexpression);
             walk_list!(visitor, visit_arm, arms);
         }
-        ExprClosure(_, ref function_declaration, body, _fn_decl_span) => {
+        ExprClosure(_, ref function_declaration, body, _fn_decl_span, _gen) => {
             visitor.visit_fn(FnKind::Closure(&expression.attrs),
                              function_declaration,
                              body,
@@ -1042,6 +1042,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
                 visitor.visit_expr(input)
             }
         }
+        ExprYield(ref subexpression) => {
+            visitor.visit_expr(subexpression);
+        }
     }
 }
 
index 421a81c0d23422f572965c2d8edfffd20bef961b..c9b5aaf3877c210faf7caa9708d09da426822445 100644 (file)
@@ -92,6 +92,8 @@ pub struct LoweringContext<'a> {
     trait_impls: BTreeMap<DefId, Vec<NodeId>>,
     trait_default_impl: BTreeMap<DefId, NodeId>,
 
+    is_generator: bool,
+
     catch_scopes: Vec<NodeId>,
     loop_scopes: Vec<NodeId>,
     is_in_loop_condition: bool,
@@ -144,6 +146,7 @@ pub fn lower_crate(sess: &Session,
         current_hir_id_owner: vec![(CRATE_DEF_INDEX, 0)],
         item_local_id_counters: NodeMap(),
         node_id_to_hir_id: IndexVec::new(),
+        is_generator: false,
     }.lower_crate(krate)
 }
 
@@ -368,6 +371,7 @@ fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>)
             arguments: decl.map_or(hir_vec![], |decl| {
                 decl.inputs.iter().map(|x| self.lower_arg(x)).collect()
             }),
+            is_generator: self.is_generator,
             value,
         };
         let id = body.id();
@@ -425,6 +429,16 @@ fn with_catch_scope<T, F>(&mut self, catch_id: NodeId, f: F) -> T
         result
     }
 
+    fn lower_body<F>(&mut self, decl: Option<&FnDecl>, f: F) -> hir::BodyId
+        where F: FnOnce(&mut LoweringContext) -> hir::Expr
+    {
+        let prev = mem::replace(&mut self.is_generator, false);
+        let result = f(self);
+        let r = self.record_body(result, decl);
+        self.is_generator = prev;
+        return r
+    }
+
     fn with_loop_scope<T, F>(&mut self, loop_id: NodeId, f: F) -> T
         where F: FnOnce(&mut LoweringContext) -> T
     {
@@ -609,13 +623,12 @@ fn lower_ty(&mut self, t: &Ty) -> P<hir::Ty> {
                 })))
             }
             TyKind::Array(ref ty, ref length) => {
-                let length = self.lower_expr(length);
-                hir::TyArray(self.lower_ty(ty),
-                             self.record_body(length, None))
+                let length = self.lower_body(None, |this| this.lower_expr(length));
+                hir::TyArray(self.lower_ty(ty), length)
             }
             TyKind::Typeof(ref expr) => {
-                let expr = self.lower_expr(expr);
-                hir::TyTypeof(self.record_body(expr, None))
+                let expr = self.lower_body(None, |this| this.lower_expr(expr));
+                hir::TyTypeof(expr)
             }
             TyKind::TraitObject(ref bounds) => {
                 let mut lifetime_bound = None;
@@ -672,8 +685,7 @@ fn lower_variant(&mut self, v: &Variant) -> hir::Variant {
                 attrs: self.lower_attrs(&v.node.attrs),
                 data: self.lower_variant_data(&v.node.data),
                 disr_expr: v.node.disr_expr.as_ref().map(|e| {
-                    let e = self.lower_expr(e);
-                    self.record_body(e, None)
+                    self.lower_body(None, |this| this.lower_expr(e))
                 }),
             },
             span: v.span,
@@ -1287,21 +1299,21 @@ fn lower_item_kind(&mut self,
                 hir::ItemUse(path, kind)
             }
             ItemKind::Static(ref t, m, ref e) => {
-                let value = self.lower_expr(e);
+                let value = self.lower_body(None, |this| this.lower_expr(e));
                 hir::ItemStatic(self.lower_ty(t),
                                 self.lower_mutability(m),
-                                self.record_body(value, None))
+                                value)
             }
             ItemKind::Const(ref t, ref e) => {
-                let value = self.lower_expr(e);
-                hir::ItemConst(self.lower_ty(t),
-                               self.record_body(value, None))
+                let value = self.lower_body(None, |this| this.lower_expr(e));
+                hir::ItemConst(self.lower_ty(t), value)
             }
             ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
                 self.with_new_scopes(|this| {
-                    let body = this.lower_block(body, false);
-                    let body = this.expr_block(body, ThinVec::new());
-                    let body_id = this.record_body(body, Some(decl));
+                    let body_id = this.lower_body(Some(decl), |this| {
+                        let body = this.lower_block(body, false);
+                        this.expr_block(body, ThinVec::new())
+                    });
                     hir::ItemFn(this.lower_fn_decl(decl),
                                               this.lower_unsafety(unsafety),
                                               this.lower_constness(constness),
@@ -1394,8 +1406,7 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem {
                     TraitItemKind::Const(ref ty, ref default) => {
                         hir::TraitItemKind::Const(this.lower_ty(ty),
                                                   default.as_ref().map(|x| {
-                            let value = this.lower_expr(x);
-                            this.record_body(value, None)
+                            this.lower_body(None, |this| this.lower_expr(x))
                         }))
                     }
                     TraitItemKind::Method(ref sig, None) => {
@@ -1404,9 +1415,10 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem {
                                                    hir::TraitMethod::Required(names))
                     }
                     TraitItemKind::Method(ref sig, Some(ref body)) => {
-                        let body = this.lower_block(body, false);
-                        let expr = this.expr_block(body, ThinVec::new());
-                        let body_id = this.record_body(expr, Some(&sig.decl));
+                        let body_id = this.lower_body(Some(&sig.decl), |this| {
+                            let body = this.lower_block(body, false);
+                            this.expr_block(body, ThinVec::new())
+                        });
                         hir::TraitItemKind::Method(this.lower_method_sig(sig),
                                                    hir::TraitMethod::Provided(body_id))
                     }
@@ -1455,14 +1467,14 @@ fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem {
                 defaultness: this.lower_defaultness(i.defaultness, true /* [1] */),
                 node: match i.node {
                     ImplItemKind::Const(ref ty, ref expr) => {
-                        let value = this.lower_expr(expr);
-                        let body_id = this.record_body(value, None);
+                        let body_id = this.lower_body(None, |this| this.lower_expr(expr));
                         hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
                     }
                     ImplItemKind::Method(ref sig, ref body) => {
-                        let body = this.lower_block(body, false);
-                        let expr = this.expr_block(body, ThinVec::new());
-                        let body_id = this.record_body(expr, Some(&sig.decl));
+                        let body_id = this.lower_body(Some(&sig.decl), |this| {
+                            let body = this.lower_block(body, false);
+                            this.expr_block(body, ThinVec::new())
+                        });
                         hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
                     }
                     ImplItemKind::Type(ref ty) => hir::ImplItemKind::Type(this.lower_ty(ty)),
@@ -1834,8 +1846,8 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
             }
             ExprKind::Repeat(ref expr, ref count) => {
                 let expr = P(self.lower_expr(expr));
-                let count = self.lower_expr(count);
-                hir::ExprRepeat(expr, self.record_body(count, None))
+                let count = self.lower_body(None, |this| this.lower_expr(count));
+                hir::ExprRepeat(expr, count)
             }
             ExprKind::Tup(ref elts) => {
                 hir::ExprTup(elts.iter().map(|x| self.lower_expr(x)).collect())
@@ -1928,11 +1940,22 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
             ExprKind::Closure(capture_clause, ref decl, ref body, fn_decl_span) => {
                 self.with_new_scopes(|this| {
                     this.with_parent_def(e.id, |this| {
-                        let expr = this.lower_expr(body);
+                        let mut is_generator = false;
+                        let body_id = this.lower_body(Some(decl), |this| {
+                            let e = this.lower_expr(body);
+                            is_generator = this.is_generator;
+                            e
+                        });
+                        if is_generator && !decl.inputs.is_empty() {
+                            span_err!(this.sess, fn_decl_span, E0625,
+                                      "generators cannot have explicit arguments");
+                            this.sess.abort_if_errors();
+                        }
                         hir::ExprClosure(this.lower_capture_clause(capture_clause),
                                          this.lower_fn_decl(decl),
-                                         this.record_body(expr, Some(decl)),
-                                         fn_decl_span)
+                                         body_id,
+                                         fn_decl_span,
+                                         is_generator)
                     })
                 })
             }
@@ -2068,6 +2091,14 @@ fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
                 return ex;
             }
 
+            ExprKind::Yield(ref opt_expr) => {
+                self.is_generator = true;
+                let expr = opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| {
+                    self.expr(e.span, hir::ExprTup(hir_vec![]), ThinVec::new())
+                });
+                hir::ExprYield(P(expr))
+            }
+
             // Desugar ExprIfLet
             // From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
             ExprKind::IfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
index 1b7eb1585671e392b14cd0f32c3fd86cca59f640..d2888dcf6aaa4fa1350179641cc9d5077c9253c4 100644 (file)
@@ -264,7 +264,7 @@ fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A where
                 }
             },
             map::NodeExpr(e) => match e.node {
-                ast::ExprClosure(_, ref decl, block, _fn_decl_span) =>
+                ast::ExprClosure(_, ref decl, block, _fn_decl_span, _gen) =>
                     closure(ClosureParts::new(&decl, block, e.id, e.span, &e.attrs)),
                 _ => bug!("expr FnLikeNode that is not fn-like"),
             },
index 45b1d6c184101bc2511be389378070dacd0cf715..f4ca536d370bf6249e6b1df937366cb86fcf505d 100644 (file)
@@ -168,7 +168,9 @@ fn to_node(self) -> Option<Node<'hir>> {
             EntryLifetime(_, n) => NodeLifetime(n),
             EntryTyParam(_, n) => NodeTyParam(n),
             EntryVisibility(_, n) => NodeVisibility(n),
-            _ => return None
+
+            NotPresent |
+            RootCrate => return None
         })
     }
 
@@ -201,7 +203,7 @@ fn associated_body(self) -> Option<BodyId> {
 
             EntryExpr(_, expr) => {
                 match expr.node {
-                    ExprClosure(.., body, _) => Some(body),
+                    ExprClosure(.., body, _, _) => Some(body),
                     _ => None,
                 }
             }
index efe0504aa18c4525236bbc453f6cd504673a3ec2..10d34e49da49510e927df8c876aac42816cff621 100644 (file)
@@ -968,7 +968,8 @@ pub struct BodyId {
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Body {
     pub arguments: HirVec<Arg>,
-    pub value: Expr
+    pub value: Expr,
+    pub is_generator: bool,
 }
 
 impl Body {
@@ -1045,7 +1046,10 @@ pub enum Expr_ {
     /// A closure (for example, `move |a, b, c| {a + b + c}`).
     ///
     /// The final span is the span of the argument block `|...|`
-    ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span),
+    ///
+    /// This may also be a generator literal, indicated by the final boolean,
+    /// in that case there is an GeneratorClause.
+    ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, bool),
     /// A block (`{ ... }`)
     ExprBlock(P<Block>),
 
@@ -1090,6 +1094,9 @@ pub enum Expr_ {
     /// For example, `[1; 5]`. The first expression is the element
     /// to be repeated; the second is the number of times to repeat it.
     ExprRepeat(P<Expr>, BodyId),
+
+    /// A suspension point for generators. This is `yield <expr>` in Rust.
+    ExprYield(P<Expr>),
 }
 
 /// Optionally `Self`-qualified value/type path or associated extension.
index abfb00a24a115364b7f825a1ae5d1f7c1117ac1b..d819fc2f779fe6e3b19918d252b2b9b4d7957bd0 100644 (file)
@@ -1312,7 +1312,7 @@ pub fn print_expr(&mut self, expr: &hir::Expr) -> io::Result<()> {
                 }
                 self.bclose_(expr.span, indent_unit)?;
             }
-            hir::ExprClosure(capture_clause, ref decl, body, _fn_decl_span) => {
+            hir::ExprClosure(capture_clause, ref decl, body, _fn_decl_span, _gen) => {
                 self.print_capture_clause(capture_clause)?;
 
                 self.print_closure_args(&decl, body)?;
@@ -1461,6 +1461,10 @@ pub fn print_expr(&mut self, expr: &hir::Expr) -> io::Result<()> {
 
                 self.pclose()?;
             }
+            hir::ExprYield(ref expr) => {
+                self.s.word("yield")?;
+                self.print_expr(&expr)?;
+            }
         }
         self.ann.post(self, NodeExpr(expr))?;
         self.end()
index b344084f580bf37e52e7a08af1f433b62191860d..ea3f04ff8c2a3c8f507a284a1347482b22bfeca2 100644 (file)
@@ -575,6 +575,7 @@ fn hash_stable<W: StableHasherResult>(&self,
                 hir::ExprBreak(..)      |
                 hir::ExprAgain(..)      |
                 hir::ExprRet(..)        |
+                hir::ExprYield(..)    |
                 hir::ExprInlineAsm(..)  |
                 hir::ExprRepeat(..)     |
                 hir::ExprTup(..)        => {
@@ -639,7 +640,7 @@ fn hash_stable<W: StableHasherResult>(&self,
     ExprWhile(cond, body, label),
     ExprLoop(body, label, loop_src),
     ExprMatch(matchee, arms, match_src),
-    ExprClosure(capture_clause, decl, body_id, span),
+    ExprClosure(capture_clause, decl, body_id, span, gen),
     ExprBlock(blk),
     ExprAssign(lhs, rhs),
     ExprAssignOp(op, lhs, rhs),
@@ -653,7 +654,8 @@ fn hash_stable<W: StableHasherResult>(&self,
     ExprRet(val),
     ExprInlineAsm(asm, inputs, outputs),
     ExprStruct(path, fields, base),
-    ExprRepeat(val, times)
+    ExprRepeat(val, times),
+    ExprYield(val)
 });
 
 impl_stable_hash_for!(enum hir::LocalSource {
@@ -1026,7 +1028,8 @@ fn hash_stable<W: StableHasherResult>(&self,
 
 impl_stable_hash_for!(struct hir::Body {
     arguments,
-    value
+    value,
+    is_generator
 });
 
 impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::BodyId {
index faf579186e5fcd7d9d9ea9f3c251efb578a54b4f..7ee2cc3b650c5c59a9bc069b9c40ba390e23c803 100644 (file)
@@ -17,7 +17,7 @@
                                            StableHasherResult};
 use std::mem;
 
-
+impl_stable_hash_for!(struct mir::GeneratorLayout<'tcx> { fields });
 impl_stable_hash_for!(struct mir::SourceInfo { span, scope });
 impl_stable_hash_for!(enum mir::Mutability { Mut, Not });
 impl_stable_hash_for!(enum mir::BorrowKind { Shared, Unique, Mut });
@@ -27,6 +27,7 @@
     ty,
     name,
     source_info,
+    internal,
     is_user_variable
 });
 impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref });
@@ -54,9 +55,11 @@ fn hash_stable<W: StableHasherResult>(&self,
             mir::TerminatorKind::SwitchInt { .. } |
             mir::TerminatorKind::Resume |
             mir::TerminatorKind::Return |
+            mir::TerminatorKind::GeneratorDrop |
             mir::TerminatorKind::Unreachable |
             mir::TerminatorKind::Drop { .. } |
             mir::TerminatorKind::DropAndReplace { .. } |
+            mir::TerminatorKind::Yield { .. } |
             mir::TerminatorKind::Call { .. } => false,
         };
 
@@ -146,6 +149,7 @@ fn hash_stable<W: StableHasherResult>(&self,
             }
             mir::TerminatorKind::Resume |
             mir::TerminatorKind::Return |
+            mir::TerminatorKind::GeneratorDrop |
             mir::TerminatorKind::Unreachable => {}
             mir::TerminatorKind::Drop { ref location, target, unwind } => {
                 location.hash_stable(hcx, hasher);
@@ -161,6 +165,13 @@ fn hash_stable<W: StableHasherResult>(&self,
                 target.hash_stable(hcx, hasher);
                 unwind.hash_stable(hcx, hasher);
             }
+            mir::TerminatorKind::Yield { ref value,
+                                        resume,
+                                        drop } => {
+                value.hash_stable(hcx, hasher);
+                resume.hash_stable(hcx, hasher);
+                drop.hash_stable(hcx, hasher);
+            }
             mir::TerminatorKind::Call { ref func,
                                         ref args,
                                         ref destination,
@@ -200,6 +211,8 @@ fn hash_stable<W: StableHasherResult>(&self,
             mir::AssertMessage::Math(ref const_math_err) => {
                 const_math_err.hash_stable(hcx, hasher);
             }
+            mir::AssertMessage::GeneratorResumedAfterReturn => (),
+            mir::AssertMessage::GeneratorResumedAfterPanic => (),
         }
     }
 }
@@ -433,6 +446,11 @@ fn hash_stable<W: StableHasherResult>(&self,
                 def_id.hash_stable(hcx, hasher);
                 substs.hash_stable(hcx, hasher);
             }
+            mir::AggregateKind::Generator(def_id, ref substs, ref interior) => {
+                def_id.hash_stable(hcx, hasher);
+                substs.hash_stable(hcx, hasher);
+                interior.hash_stable(hcx, hasher);
+            }
         }
     }
 }
index 8a37d7bab4445c9314b23a49ce04aeb20132aa78..22364c2c43a366812dfffac7511afbfe205ba2a2 100644 (file)
@@ -147,6 +147,11 @@ fn hash_stable<W: StableHasherResult>(&self,
     }
 }
 
+impl_stable_hash_for!(struct ty::GenSig<'tcx> {
+    yield_ty,
+    return_ty
+});
+
 impl_stable_hash_for!(struct ty::FnSig<'tcx> {
     inputs_and_output,
     variadic,
@@ -321,6 +326,8 @@ fn hash_stable<W: StableHasherResult>(&self,
 
 impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs });
 
+impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness });
+
 impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> {
     parent,
     predicates
@@ -546,6 +553,12 @@ fn hash_stable<W: StableHasherResult>(&self,
                 def_id.hash_stable(hcx, hasher);
                 closure_substs.hash_stable(hcx, hasher);
             }
+            TyGenerator(def_id, closure_substs, interior)
+             => {
+                def_id.hash_stable(hcx, hasher);
+                closure_substs.hash_stable(hcx, hasher);
+                interior.hash_stable(hcx, hasher);
+            }
             TyTuple(inner_tys, from_diverging_type_var) => {
                 inner_tys.hash_stable(hcx, hasher);
                 from_diverging_type_var.hash_stable(hcx, hasher);
@@ -626,6 +639,8 @@ fn hash_stable<W: StableHasherResult>(&self,
             ref upvar_capture_map,
             ref closure_tys,
             ref closure_kinds,
+            ref generator_interiors,
+            ref generator_sigs,
             ref liberated_fn_sigs,
             ref fru_field_types,
 
@@ -655,6 +670,8 @@ fn hash_stable<W: StableHasherResult>(&self,
 
             ich::hash_stable_nodemap(hcx, hasher, closure_tys);
             ich::hash_stable_nodemap(hcx, hasher, closure_kinds);
+            ich::hash_stable_nodemap(hcx, hasher, generator_interiors);
+            ich::hash_stable_nodemap(hcx, hasher, generator_sigs);
             ich::hash_stable_nodemap(hcx, hasher, liberated_fn_sigs);
             ich::hash_stable_nodemap(hcx, hasher, fru_field_types);
             ich::hash_stable_nodemap(hcx, hasher, cast_kinds);
@@ -683,6 +700,7 @@ fn hash_stable<W: StableHasherResult>(&self,
     TupleSimplifiedType(size),
     TraitSimplifiedType(def_id),
     ClosureSimplifiedType(def_id),
+    GeneratorSimplifiedType(def_id),
     AnonSimplifiedType(def_id),
     FunctionSimplifiedType(params),
     ParameterSimplifiedType
index 41858088f7e704865ca70d10006654482efcb634..b8b5a55f57806eaf9cee92cb70e7be71175fc771 100644 (file)
@@ -166,6 +166,7 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
             ty::TyFnPtr(_) |
             ty::TyDynamic(..) |
             ty::TyClosure(..) |
+            ty::TyGenerator(..) |
             ty::TyNever |
             ty::TyTuple(..) |
             ty::TyProjection(..) |
index bf79becfe4a1077582efab02196ef529a4330c24..6b60a248267171a91c44d8872a9f6fc2ac5159c4 100644 (file)
@@ -1361,6 +1361,18 @@ pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
 
         self.tcx.fn_sig(def_id)
     }
+
+    pub fn generator_sig(&self, def_id: DefId) -> Option<ty::PolyGenSig<'tcx>> {
+        if let Some(tables) = self.in_progress_tables {
+            if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
+                if let Some(&ty) = tables.borrow().generator_sigs.get(&id) {
+                    return ty.map(|t| ty::Binder(t));
+                }
+            }
+        }
+
+        self.tcx.generator_sig(def_id)
+    }
 }
 
 impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {
index 87e933e85e2289526a507535e5adb867f6abf905..1ea5b29b15bab108ef97d4e5d46d69993d452392 100644 (file)
@@ -517,13 +517,17 @@ pub fn walk_expr(&mut self, expr: &hir::Expr) {
                 self.consume_expr(&base);
             }
 
-            hir::ExprClosure(.., fn_decl_span) => {
+            hir::ExprClosure(.., fn_decl_span, _) => {
                 self.walk_captures(expr, fn_decl_span)
             }
 
             hir::ExprBox(ref base) => {
                 self.consume_expr(&base);
             }
+
+            hir::ExprYield(ref value) => {
+                self.consume_expr(&value);
+            }
         }
     }
 
index 01ed79096b10196e107efe4f9d8c8fc0b4faf64c..a36828ffcc93c8bf63e3972b08847e2265d28fc2 100644 (file)
@@ -315,6 +315,9 @@ pub fn collect_language_items(session: &Session,
     FnMutTraitLangItem,              "fn_mut",                  fn_mut_trait;
     FnOnceTraitLangItem,             "fn_once",                 fn_once_trait;
 
+    GeneratorStateLangItem,          "generator_state",         gen_state;
+    GeneratorTraitLangItem,          "generator",               gen_trait;
+
     EqTraitLangItem,                 "eq",                      eq_trait;
     OrdTraitLangItem,                "ord",                     ord_trait;
 
index 070ad5159086e1607f9ae63ea5798cb0965322c2..a432e49dbaf0f05374c96f79eeb9dd81bc1e82c9 100644 (file)
@@ -460,7 +460,7 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
       hir::ExprAgain(_) | hir::ExprLit(_) | hir::ExprRet(..) |
       hir::ExprBlock(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) |
       hir::ExprStruct(..) | hir::ExprRepeat(..) |
-      hir::ExprInlineAsm(..) | hir::ExprBox(..) |
+      hir::ExprInlineAsm(..) | hir::ExprBox(..) | hir::ExprYield(..) |
       hir::ExprType(..) | hir::ExprPath(hir::QPath::TypeRelative(..)) => {
           intravisit::walk_expr(ir, expr);
       }
@@ -881,7 +881,6 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
 
         match expr.node {
           // Interesting cases with control flow or which gen/kill
-
           hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
               self.access_path(expr.id, path, succ, ACC_READ | ACC_USE)
           }
@@ -894,7 +893,7 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
               self.propagate_through_expr(&e, succ)
           }
 
-          hir::ExprClosure(.., blk_id, _) => {
+          hir::ExprClosure(.., blk_id, _, _) => {
               debug!("{} is an ExprClosure", self.ir.tcx.hir.node_to_pretty_string(expr.id));
 
               /*
@@ -1116,6 +1115,7 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
           hir::ExprCast(ref e, _) |
           hir::ExprType(ref e, _) |
           hir::ExprUnary(_, ref e) |
+          hir::ExprYield(ref e) |
           hir::ExprRepeat(ref e, _) => {
             self.propagate_through_expr(&e, succ)
           }
@@ -1224,18 +1224,23 @@ fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
         }
     }
 
+    fn access_var(&mut self, id: NodeId, nid: NodeId, succ: LiveNode, acc: u32, span: Span)
+                  -> LiveNode {
+        let ln = self.live_node(id, span);
+        if acc != 0 {
+            self.init_from_succ(ln, succ);
+            let var = self.variable(nid, span);
+            self.acc(ln, var, acc);
+        }
+        ln
+    }
+
     fn access_path(&mut self, id: NodeId, path: &hir::Path, succ: LiveNode, acc: u32)
                    -> LiveNode {
         match path.def {
           Def::Local(def_id) => {
             let nid = self.ir.tcx.hir.as_local_node_id(def_id).unwrap();
-            let ln = self.live_node(id, path.span);
-            if acc != 0 {
-                self.init_from_succ(ln, succ);
-                let var = self.variable(nid, path.span);
-                self.acc(ln, var, acc);
-            }
-            ln
+            self.access_var(id, nid, succ, acc, path.span)
           }
           _ => succ
         }
@@ -1398,7 +1403,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
       hir::ExprBreak(..) | hir::ExprAgain(..) | hir::ExprLit(_) |
       hir::ExprBlock(..) | hir::ExprAddrOf(..) |
       hir::ExprStruct(..) | hir::ExprRepeat(..) |
-      hir::ExprClosure(..) | hir::ExprPath(_) |
+      hir::ExprClosure(..) | hir::ExprPath(_) | hir::ExprYield(..) |
       hir::ExprBox(..) | hir::ExprType(..) => {
         intravisit::walk_expr(this, expr);
       }
index b4993aafc4c9ed379f692e6848b1e74d4890e4b7..3abd63fccdb382db9a307150f8f0183613579e39 100644 (file)
@@ -615,7 +615,7 @@ pub fn cat_expr_unadjusted(&self, expr: &hir::Expr) -> McResult<cmt<'tcx>> {
           hir::ExprAddrOf(..) | hir::ExprCall(..) |
           hir::ExprAssign(..) | hir::ExprAssignOp(..) |
           hir::ExprClosure(..) | hir::ExprRet(..) |
-          hir::ExprUnary(..) |
+          hir::ExprUnary(..) | hir::ExprYield(..) |
           hir::ExprMethodCall(..) | hir::ExprCast(..) |
           hir::ExprArray(..) | hir::ExprTup(..) | hir::ExprIf(..) |
           hir::ExprBinary(..) | hir::ExprWhile(..) |
@@ -709,7 +709,13 @@ fn cat_upvar(&self,
 
         let kind = match self.tables.closure_kinds.get(&fn_node_id) {
             Some(&(kind, _)) => kind,
-            None => span_bug!(span, "missing closure kind")
+            None => {
+                let ty = self.node_ty(fn_node_id)?;
+                match ty.sty {
+                    ty::TyGenerator(..) => ty::ClosureKind::FnOnce,
+                    _ => span_bug!(span, "missing closure kind"),
+                }
+            }
         };
 
         let upvar_id = ty::UpvarId { var_id,
index 9133a5e777db2743deea169f9a4a943dd5c27eb7..9d28e2f496dfc0b4a8be06779ff5a4a45e0a33fa 100644 (file)
@@ -24,6 +24,7 @@
 use std::rc::Rc;
 use syntax::codemap;
 use syntax::ast;
+use syntax::ast::NodeId;
 use syntax_pos::Span;
 use ty::TyCtxt;
 use ty::maps::Providers;
@@ -32,6 +33,7 @@
 use hir::def_id::DefId;
 use hir::intravisit::{self, Visitor, NestedVisitorMap};
 use hir::{Block, Arm, Pat, PatKind, Stmt, Expr, Local};
+use hir::map::Node;
 use mir::transform::MirSource;
 
 /// CodeExtent represents a statically-describable extent that can be
@@ -789,7 +791,7 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
     match expr.node {
         // Manually recurse over closures, because they are the only
         // case of nested bodies that share the parent environment.
-        hir::ExprClosure(.., body, _) => {
+        hir::ExprClosure(.., body, _, _) => {
             let body = visitor.tcx.hir.body(body);
             visitor.visit_body(body);
         }
@@ -1166,6 +1168,97 @@ fn region_maps<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
     Rc::new(maps)
 }
 
+struct YieldFinder<'a> {
+    cache: &'a mut FxHashMap<NodeId, Option<Span>>,
+    result: Option<Span>,
+}
+
+impl<'a> YieldFinder<'a> {
+    fn lookup<F: FnOnce(&mut Self)>(&mut self, id: NodeId, f: F) {
+        if let Some(result) = self.cache.get(&id) {
+            self.result = *result;
+            return;
+        }
+        if self.result.is_some() {
+            return;
+        }
+        f(self);
+        self.cache.insert(id, self.result);
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for YieldFinder<'a> {
+    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+        if let hir::ExprYield(..) = expr.node {
+            self.result = Some(expr.span);
+            return;
+        }
+
+        self.lookup(expr.id, |this| {
+            intravisit::walk_expr(this, expr);
+        });
+    }
+
+    fn visit_block(&mut self, block: &'tcx hir::Block) {
+        self.lookup(block.id, |this| {
+            intravisit::walk_block(this, block);
+        });
+    }
+}
+
+impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
+    /// Checks whether the given code extent contains a `yield`. If so,
+    /// returns `Some(span)` with the span of a yield we found.
+    pub fn yield_in_extent(self,
+                          extent: CodeExtent,
+                          cache: &mut FxHashMap<NodeId, Option<Span>>) -> Option<Span> {
+        let mut finder = YieldFinder {
+            cache,
+            result: None,
+        };
+
+        match extent {
+            CodeExtent::DestructionScope(node_id) |
+            CodeExtent::Misc(node_id) => {
+                match self.hir.get(node_id) {
+                    Node::NodeItem(_) |
+                    Node::NodeTraitItem(_) |
+                    Node::NodeImplItem(_) => {
+                        let body = self.hir.body(self.hir.body_owned_by(node_id));
+                        finder.visit_body(body);
+                    }
+                    Node::NodeExpr(expr) => finder.visit_expr(expr),
+                    Node::NodeStmt(stmt) => finder.visit_stmt(stmt),
+                    Node::NodeBlock(block) => finder.visit_block(block),
+                    _ => bug!(),
+                }
+            }
+
+            CodeExtent::CallSiteScope(body_id) |
+            CodeExtent::ParameterScope(body_id) => {
+                finder.visit_body(self.hir.body(body_id))
+            }
+
+            CodeExtent::Remainder(r) => {
+                if let Node::NodeBlock(block) = self.hir.get(r.block) {
+                    for stmt in &block.stmts[(r.first_statement_index as usize + 1)..] {
+                        finder.visit_stmt(stmt);
+                    }
+                    block.expr.as_ref().map(|e| finder.visit_expr(e));
+                } else {
+                    bug!()
+                }
+            }
+        }
+
+        finder.result
+    }
+}
+
 pub fn provide(providers: &mut Providers) {
     *providers = Providers {
         region_maps,
index 1e8dda0addf4cb8a42f0caec95668a758c766393..6484572d2243c0fd85ce8c002a30d8d629b42d45 100644 (file)
@@ -21,7 +21,7 @@
 use hir::def::CtorKind;
 use hir::def_id::DefId;
 use ty::subst::{Subst, Substs};
-use ty::{self, AdtDef, ClosureSubsts, Region, Ty};
+use ty::{self, AdtDef, ClosureSubsts, Region, Ty, GeneratorInterior};
 use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
 use util::ppaux;
 use rustc_back::slice;
@@ -104,6 +104,15 @@ pub struct Mir<'tcx> {
     /// Return type of the function.
     pub return_ty: Ty<'tcx>,
 
+    /// Yield type of the function, if it is a generator.
+    pub yield_ty: Option<Ty<'tcx>>,
+
+    /// Generator drop glue
+    pub generator_drop: Option<Box<Mir<'tcx>>>,
+
+    /// The layout of a generator. Produced by the state transformation.
+    pub generator_layout: Option<GeneratorLayout<'tcx>>,
+
     /// Declarations of locals.
     ///
     /// The first local is the return value pointer, followed by `arg_count`
@@ -144,6 +153,7 @@ pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
                visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
                promoted: IndexVec<Promoted, Mir<'tcx>>,
                return_ty: Ty<'tcx>,
+               yield_ty: Option<Ty<'tcx>>,
                local_decls: IndexVec<Local, LocalDecl<'tcx>>,
                arg_count: usize,
                upvar_decls: Vec<UpvarDecl>,
@@ -159,6 +169,9 @@ pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
             visibility_scopes,
             promoted,
             return_ty,
+            yield_ty,
+            generator_drop: None,
+            generator_layout: None,
             local_decls,
             arg_count,
             upvar_decls,
@@ -270,6 +283,9 @@ pub fn make_statement_nop(&mut self, location: Location) {
     visibility_scopes,
     promoted,
     return_ty,
+    yield_ty,
+    generator_drop,
+    generator_layout,
     local_decls,
     arg_count,
     upvar_decls,
@@ -395,6 +411,16 @@ pub struct LocalDecl<'tcx> {
     /// True if this corresponds to a user-declared local variable.
     pub is_user_variable: bool,
 
+    /// True if this is an internal local.
+    /// These locals are not based on types in the source code and are only used
+    /// for drop flags at the moment.
+    /// The generator transformation will sanity check the locals which are live across
+    /// a suspension point against the type components of the generator which
+    /// type checking knows are live across a suspension point.
+    /// We need to flag drop flags to avoid triggering this check as they are introduced
+    /// after typeck.
+    pub internal: bool,
+
     /// Type of this local.
     pub ty: Ty<'tcx>,
 
@@ -420,6 +446,23 @@ pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self {
                 span,
                 scope: ARGUMENT_VISIBILITY_SCOPE
             },
+            internal: false,
+            is_user_variable: false
+        }
+    }
+
+    /// Create a new `LocalDecl` for a internal temporary.
+    #[inline]
+    pub fn new_internal(ty: Ty<'tcx>, span: Span) -> Self {
+        LocalDecl {
+            mutability: Mutability::Mut,
+            ty,
+            name: None,
+            source_info: SourceInfo {
+                span,
+                scope: ARGUMENT_VISIBILITY_SCOPE
+            },
+            internal: true,
             is_user_variable: false
         }
     }
@@ -436,6 +479,7 @@ pub fn new_return_pointer(return_ty: Ty, span: Span) -> LocalDecl {
                 span,
                 scope: ARGUMENT_VISIBILITY_SCOPE
             },
+            internal: false,
             name: None,     // FIXME maybe we do want some name here?
             is_user_variable: false
         }
@@ -567,7 +611,20 @@ pub enum TerminatorKind<'tcx> {
         msg: AssertMessage<'tcx>,
         target: BasicBlock,
         cleanup: Option<BasicBlock>
-    }
+    },
+
+    /// A suspend point
+    Yield {
+        /// The value to return
+        value: Operand<'tcx>,
+        /// Where to resume to
+        resume: BasicBlock,
+        /// Cleanup to be done if the generator is dropped at this suspend point
+        drop: Option<BasicBlock>,
+    },
+
+    /// Indicates the end of the dropping of a generator
+    GeneratorDrop,
 }
 
 impl<'tcx> Terminator<'tcx> {
@@ -597,7 +654,7 @@ pub fn successors(&self) -> Cow<[BasicBlock]> {
         match *self {
             Goto { target: ref b } => slice::ref_slice(b).into_cow(),
             SwitchInt { targets: ref b, .. } => b[..].into_cow(),
-            Resume => (&[]).into_cow(),
+            Resume | GeneratorDrop => (&[]).into_cow(),
             Return => (&[]).into_cow(),
             Unreachable => (&[]).into_cow(),
             Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
@@ -605,6 +662,8 @@ pub fn successors(&self) -> Cow<[BasicBlock]> {
                 slice::ref_slice(t).into_cow(),
             Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
             Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
+            Yield { resume: t, drop: Some(c), .. } => vec![t, c].into_cow(),
+            Yield { resume: ref t, drop: None, .. } => slice::ref_slice(t).into_cow(),
             DropAndReplace { target, unwind: Some(unwind), .. } |
             Drop { target, unwind: Some(unwind), .. } => {
                 vec![target, unwind].into_cow()
@@ -625,13 +684,15 @@ pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
         match *self {
             Goto { target: ref mut b } => vec![b],
             SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
-            Resume => Vec::new(),
+            Resume | GeneratorDrop => Vec::new(),
             Return => Vec::new(),
             Unreachable => Vec::new(),
             Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
             Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
             Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
             Call { destination: None, cleanup: None, .. } => vec![],
+            Yield { resume: ref mut t, drop: Some(ref mut c), .. } => vec![t, c],
+            Yield { resume: ref mut t, drop: None, .. } => vec![t],
             DropAndReplace { ref mut target, unwind: Some(ref mut unwind), .. } |
             Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind],
             DropAndReplace { ref mut target, unwind: None, .. } |
@@ -664,6 +725,14 @@ pub fn terminator(&self) -> &Terminator<'tcx> {
     pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> {
         self.terminator.as_mut().expect("invalid terminator state")
     }
+
+    pub fn retain_statements<F>(&mut self, mut f: F) where F: FnMut(&mut Statement) -> bool {
+        for s in &mut self.statements {
+            if !f(s) {
+                s.kind = StatementKind::Nop;
+            }
+        }
+    }
 }
 
 impl<'tcx> Debug for TerminatorKind<'tcx> {
@@ -703,7 +772,9 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
             Goto { .. } => write!(fmt, "goto"),
             SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
             Return => write!(fmt, "return"),
+            GeneratorDrop => write!(fmt, "generator_drop"),
             Resume => write!(fmt, "resume"),
+            Yield { ref value, .. } => write!(fmt, "_1 = suspend({:?})", value),
             Unreachable => write!(fmt, "unreachable"),
             Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
             DropAndReplace { ref location, ref value, .. } =>
@@ -737,6 +808,12 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
                     AssertMessage::Math(ref err) => {
                         write!(fmt, "{:?}", err.description())?;
                     }
+                    AssertMessage::GeneratorResumedAfterReturn => {
+                        write!(fmt, "{:?}", "generator resumed after completion")?;
+                    }
+                    AssertMessage::GeneratorResumedAfterPanic => {
+                        write!(fmt, "{:?}", "generator resumed after panicking")?;
+                    }
                 }
 
                 write!(fmt, ")")
@@ -748,7 +825,7 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
         use self::TerminatorKind::*;
         match *self {
-            Return | Resume | Unreachable => vec![],
+            Return | Resume | Unreachable | GeneratorDrop => vec![],
             Goto { .. } => vec!["".into()],
             SwitchInt { ref values, .. } => {
                 values.iter()
@@ -765,6 +842,9 @@ pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
             Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
             Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
             Call { destination: None, cleanup: None, .. } => vec![],
+            Yield { drop: Some(_), .. } =>
+                vec!["resume".into_cow(), "drop".into_cow()],
+            Yield { drop: None, .. } => vec!["resume".into_cow()],
             DropAndReplace { unwind: None, .. } |
             Drop { unwind: None, .. } => vec!["return".into_cow()],
             DropAndReplace { unwind: Some(_), .. } |
@@ -784,7 +864,9 @@ pub enum AssertMessage<'tcx> {
         len: Operand<'tcx>,
         index: Operand<'tcx>
     },
-    Math(ConstMathErr)
+    Math(ConstMathErr),
+    GeneratorResumedAfterReturn,
+    GeneratorResumedAfterPanic,
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -1178,6 +1260,7 @@ pub enum AggregateKind<'tcx> {
     /// number and is present only for union expressions.
     Adt(&'tcx AdtDef, usize, &'tcx Substs<'tcx>, Option<usize>),
     Closure(DefId, ClosureSubsts<'tcx>),
+    Generator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>),
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
@@ -1339,6 +1422,31 @@ fn fmt_tuple(fmt: &mut Formatter, lvs: &[Operand]) -> fmt::Result {
                             write!(fmt, "[closure]")
                         }
                     }),
+
+                    AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
+                        if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
+                            let name = format!("[generator@{:?}]", tcx.hir.span(node_id));
+                            let mut struct_fmt = fmt.debug_struct(&name);
+
+                            tcx.with_freevars(node_id, |freevars| {
+                                for (freevar, lv) in freevars.iter().zip(lvs) {
+                                    let def_id = freevar.def.def_id();
+                                    let var_id = tcx.hir.as_local_node_id(def_id).unwrap();
+                                    let var_name = tcx.local_var_name_str(var_id);
+                                    struct_fmt.field(&var_name, lv);
+                                }
+                                struct_fmt.field("$state", &lvs[freevars.len()]);
+                                for i in (freevars.len() + 1)..lvs.len() {
+                                    struct_fmt.field(&format!("${}", i - freevars.len() - 1),
+                                                     &lvs[i]);
+                                }
+                            });
+
+                            struct_fmt.finish()
+                        } else {
+                            write!(fmt, "[generator]")
+                        }
+                    }),
                 }
             }
         }
@@ -1483,6 +1591,11 @@ pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -
     }
 }
 
+/// The layout of generator state
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct GeneratorLayout<'tcx> {
+    pub fields: Vec<LocalDecl<'tcx>>,
+}
 
 /*
  * TypeFoldable implementations for MIR types
@@ -1495,6 +1608,9 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
             visibility_scopes: self.visibility_scopes.clone(),
             promoted: self.promoted.fold_with(folder),
             return_ty: self.return_ty.fold_with(folder),
+            yield_ty: self.yield_ty.fold_with(folder),
+            generator_drop: self.generator_drop.fold_with(folder),
+            generator_layout: self.generator_layout.fold_with(folder),
             local_decls: self.local_decls.fold_with(folder),
             arg_count: self.arg_count,
             upvar_decls: self.upvar_decls.clone(),
@@ -1506,12 +1622,27 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
 
     fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
         self.basic_blocks.visit_with(visitor) ||
+        self.generator_drop.visit_with(visitor) ||
+        self.generator_layout.visit_with(visitor) ||
+        self.yield_ty.visit_with(visitor) ||
         self.promoted.visit_with(visitor)     ||
         self.return_ty.visit_with(visitor)    ||
         self.local_decls.visit_with(visitor)
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        GeneratorLayout {
+            fields: self.fields.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.fields.visit_with(visitor)
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for LocalDecl<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         LocalDecl {
@@ -1638,6 +1769,11 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
                 target,
                 unwind,
             },
+            Yield { ref value, resume, drop } => Yield {
+                value: value.fold_with(folder),
+                resume: resume,
+                drop: drop,
+            },
             Call { ref func, ref args, ref destination, cleanup } => {
                 let dest = destination.as_ref().map(|&(ref loc, dest)| {
                     (loc.fold_with(folder), dest)
@@ -1667,6 +1803,7 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
                     cleanup,
                 }
             },
+            GeneratorDrop => GeneratorDrop,
             Resume => Resume,
             Return => Return,
             Unreachable => Unreachable,
@@ -1686,6 +1823,8 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
             Drop { ref location, ..} => location.visit_with(visitor),
             DropAndReplace { ref location, ref value, ..} =>
                 location.visit_with(visitor) || value.visit_with(visitor),
+            Yield { ref value, ..} =>
+                value.visit_with(visitor),
             Call { ref func, ref args, ref destination, .. } => {
                 let dest = if let Some((ref loc, _)) = *destination {
                     loc.visit_with(visitor)
@@ -1706,6 +1845,7 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
             Goto { .. } |
             Resume |
             Return |
+            GeneratorDrop |
             Unreachable => false
         }
     }
@@ -1751,7 +1891,11 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
                     AggregateKind::Adt(def, v, substs, n) =>
                         AggregateKind::Adt(def, v, substs.fold_with(folder), n),
                     AggregateKind::Closure(id, substs) =>
-                        AggregateKind::Closure(id, substs.fold_with(folder))
+                        AggregateKind::Closure(id, substs.fold_with(folder)),
+                    AggregateKind::Generator(id, substs, interior) =>
+                        AggregateKind::Generator(id,
+                                                 substs.fold_with(folder),
+                                                 interior.fold_with(folder)),
                 };
                 Aggregate(kind, fields.fold_with(folder))
             }
@@ -1777,7 +1921,9 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
                     AggregateKind::Array(ty) => ty.visit_with(visitor),
                     AggregateKind::Tuple => false,
                     AggregateKind::Adt(_, _, substs, _) => substs.visit_with(visitor),
-                    AggregateKind::Closure(_, substs) => substs.visit_with(visitor)
+                    AggregateKind::Closure(_, substs) => substs.visit_with(visitor),
+                    AggregateKind::Generator(_, substs, interior) => substs.visit_with(visitor) ||
+                        interior.visit_with(visitor),
                 }) || fields.visit_with(visitor)
             }
         }
index a801804d502a141567a0b78c199d3414c73fa821..eb403442f46399de4b82d559a70e7562afb4b4f4 100644 (file)
@@ -207,6 +207,9 @@ pub fn ty<'a, 'gcx, D>(&self, local_decls: &D, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> T
                     AggregateKind::Closure(did, substs) => {
                         tcx.mk_closure_from_closure_substs(did, substs)
                     }
+                    AggregateKind::Generator(did, substs, interior) => {
+                        tcx.mk_generator(did, substs, interior)
+                    }
                 }
             }
         }
index aa91123ef6952ac044781b2aac0369725292fd5e..f29405e6650518bced752cc45950d6699cdf61d7 100644 (file)
@@ -33,7 +33,10 @@ pub enum MirSource {
     Static(NodeId, hir::Mutability),
 
     /// Promoted rvalues within a function.
-    Promoted(NodeId, Promoted)
+    Promoted(NodeId, Promoted),
+
+    /// Drop glue for a generator.
+    GeneratorDrop(NodeId),
 }
 
 impl<'a, 'tcx> MirSource {
@@ -70,6 +73,7 @@ pub fn item_id(&self) -> NodeId {
         match *self {
             MirSource::Fn(id) |
             MirSource::Const(id) |
+            MirSource::GeneratorDrop(id) |
             MirSource::Static(id, _) |
             MirSource::Promoted(id, _) => id
         }
index a6d115bf0391280e17c162b01ca146d9858b5674..6589e824187fd5b730c11b12166d249ef5aae044 100644 (file)
@@ -11,7 +11,7 @@
 use middle::const_val::ConstVal;
 use hir::def_id::DefId;
 use ty::subst::Substs;
-use ty::{ClosureSubsts, Region, Ty};
+use ty::{ClosureSubsts, Region, Ty, GeneratorInterior};
 use mir::*;
 use rustc_const_math::ConstUsize;
 use syntax_pos::Span;
@@ -226,6 +226,12 @@ fn visit_closure_substs(&mut self,
                 self.super_closure_substs(substs);
             }
 
+            fn visit_generator_interior(&mut self,
+                                    interior: & $($mutability)* GeneratorInterior<'tcx>,
+                                    _: Location) {
+                self.super_generator_interior(interior);
+            }
+
             fn visit_const_val(&mut self,
                                const_val: & $($mutability)* ConstVal,
                                _: Location) {
@@ -249,6 +255,10 @@ fn visit_local_decl(&mut self,
                 self.super_local_decl(local_decl);
             }
 
+            fn visit_local(&mut self,
+                                _local: & $($mutability)* Local) {
+            }
+
             fn visit_visibility_scope(&mut self,
                                       scope: & $($mutability)* VisibilityScope) {
                 self.super_visibility_scope(scope);
@@ -415,6 +425,7 @@ fn super_terminator_kind(&mut self,
 
                     TerminatorKind::Resume |
                     TerminatorKind::Return |
+                    TerminatorKind::GeneratorDrop |
                     TerminatorKind::Unreachable => {
                     }
 
@@ -461,6 +472,15 @@ fn super_terminator_kind(&mut self,
                         self.visit_branch(block, target);
                         cleanup.map(|t| self.visit_branch(block, t));
                     }
+
+                    TerminatorKind::Yield { ref $($mutability)* value,
+                                              resume,
+                                              drop } => {
+                        self.visit_operand(value, source_location);
+                        self.visit_branch(block, resume);
+                        drop.map(|t| self.visit_branch(block, t));
+                    }
+
                 }
             }
 
@@ -475,7 +495,9 @@ fn super_assert_message(&mut self,
                         self.visit_operand(len, location);
                         self.visit_operand(index, location);
                     }
-                    AssertMessage::Math(_) => {}
+                    AssertMessage::Math(_) => {},
+                    AssertMessage::GeneratorResumedAfterReturn => {},
+                    AssertMessage::GeneratorResumedAfterPanic => {},
                 }
             }
 
@@ -553,6 +575,13 @@ fn super_rvalue(&mut self,
                                 self.visit_def_id(def_id, location);
                                 self.visit_closure_substs(closure_substs, location);
                             }
+                            AggregateKind::Generator(ref $($mutability)* def_id,
+                                                   ref $($mutability)* closure_substs,
+                                                   ref $($mutability)* interior) => {
+                                self.visit_def_id(def_id, location);
+                                self.visit_closure_substs(closure_substs, location);
+                                self.visit_generator_interior(interior, location);
+                            }
                         }
 
                         for operand in operands {
@@ -580,7 +609,8 @@ fn super_lvalue(&mut self,
                             context: LvalueContext<'tcx>,
                             location: Location) {
                 match *lvalue {
-                    Lvalue::Local(_) => {
+                    Lvalue::Local(ref $($mutability)* local) => {
+                        self.visit_local(local);
                     }
                     Lvalue::Static(ref $($mutability)* static_) => {
                         self.visit_static(static_, context, location);
@@ -651,6 +681,7 @@ fn super_local_decl(&mut self,
                     ref $($mutability)* ty,
                     name: _,
                     ref $($mutability)* source_info,
+                    internal: _,
                     is_user_variable: _,
                 } = *local_decl;
 
@@ -719,6 +750,10 @@ fn super_ty(&mut self, _ty: & $($mutability)* Ty<'tcx>) {
             fn super_substs(&mut self, _substs: & $($mutability)* &'tcx Substs<'tcx>) {
             }
 
+            fn super_generator_interior(&mut self,
+                                    _interior: & $($mutability)* GeneratorInterior<'tcx>) {
+            }
+
             fn super_closure_substs(&mut self,
                                     _substs: & $($mutability)* ClosureSubsts<'tcx>) {
             }
index 34df447a11e154b4b19d5d13673a3ab6f7e1845a..431bd8ee88f709883aa72813689aa76cee731c72 100644 (file)
@@ -301,7 +301,7 @@ fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool {
             true
         }
 
-        ty::TyClosure(..) | ty::TyAnon(..) => {
+        ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAnon(..) => {
             bug!("ty_is_local invoked on unexpected type: {:?}", ty)
         }
     }
index f0fc6998c9e51735931516c79069d979302ebd6e..e4042c48bf332e612c8d8540270cd06c10e3c7d7 100644 (file)
@@ -251,6 +251,7 @@ fn type_category<'tcx>(t: Ty<'tcx>) -> Option<u32> {
                     AdtKind::Union => Some(16),
                     AdtKind::Enum => Some(17),
                 },
+                ty::TyGenerator(..) => Some(18),
                 ty::TyInfer(..) | ty::TyError => None
             }
         }
index d1938197e652965dbf338ac1c233141f36c9e909..67c7ecda6490ebab31eae82710bcabc33ea5c23d 100644 (file)
@@ -310,6 +310,9 @@ pub enum Vtable<'tcx, N> {
 
     /// Same as above, but for a fn pointer type with the given signature.
     VtableFnPointer(VtableFnPointerData<'tcx, N>),
+
+    /// Vtable automatically generated for a generator
+    VtableGenerator(VtableGeneratorData<'tcx, N>),
 }
 
 /// Identifies a particular impl in the source, along with a set of
@@ -329,6 +332,15 @@ pub struct VtableImplData<'tcx, N> {
     pub nested: Vec<N>
 }
 
+#[derive(Clone, PartialEq, Eq)]
+pub struct VtableGeneratorData<'tcx, N> {
+    pub closure_def_id: DefId,
+    pub substs: ty::ClosureSubsts<'tcx>,
+    /// Nested obligations. This can be non-empty if the generator
+    /// signature contains associated types.
+    pub nested: Vec<N>
+}
+
 #[derive(Clone, PartialEq, Eq)]
 pub struct VtableClosureData<'tcx, N> {
     pub closure_def_id: DefId,
@@ -743,6 +755,7 @@ pub fn nested_obligations(self) -> Vec<N> {
             VtableBuiltin(i) => i.nested,
             VtableDefaultImpl(d) => d.nested,
             VtableClosure(c) => c.nested,
+            VtableGenerator(c) => c.nested,
             VtableObject(d) => d.nested,
             VtableFnPointer(d) => d.nested,
         }
@@ -754,6 +767,7 @@ fn nested_obligations_mut(&mut self) -> &mut Vec<N> {
             &mut VtableParam(ref mut n) => n,
             &mut VtableBuiltin(ref mut i) => &mut i.nested,
             &mut VtableDefaultImpl(ref mut d) => &mut d.nested,
+            &mut VtableGenerator(ref mut c) => &mut c.nested,
             &mut VtableClosure(ref mut c) => &mut c.nested,
             &mut VtableObject(ref mut d) => &mut d.nested,
             &mut VtableFnPointer(ref mut d) => &mut d.nested,
@@ -784,6 +798,11 @@ pub fn map<M, F>(self, f: F) -> Vtable<'tcx, M> where F: FnMut(N) -> M {
                 fn_ty: p.fn_ty,
                 nested: p.nested.into_iter().map(f).collect(),
             }),
+            VtableGenerator(c) => VtableGenerator(VtableGeneratorData {
+                closure_def_id: c.closure_def_id,
+                substs: c.substs,
+                nested: c.nested.into_iter().map(f).collect(),
+            }),
             VtableClosure(c) => VtableClosure(VtableClosureData {
                 closure_def_id: c.closure_def_id,
                 substs: c.substs,
index 7cce9c398bb44cfa16c8feb3e2c6239b9c723c5a..71f4c8441b2b0562f132015edf5815cd380157cc 100644 (file)
@@ -19,6 +19,7 @@
 use super::SelectionContext;
 use super::SelectionError;
 use super::VtableClosureData;
+use super::VtableGeneratorData;
 use super::VtableFnPointerData;
 use super::VtableImplData;
 use super::util;
@@ -882,6 +883,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
 
         match vtable {
             super::VtableClosure(_) |
+            super::VtableGenerator(_) |
             super::VtableFnPointer(_) |
             super::VtableObject(_) => {
                 debug!("assemble_candidates_from_impls: vtable={:?}",
@@ -1041,6 +1043,8 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
     match vtable {
         super::VtableImpl(data) =>
             confirm_impl_candidate(selcx, obligation, data),
+        super::VtableGenerator(data) =>
+            confirm_generator_candidate(selcx, obligation, data),
         super::VtableClosure(data) =>
             confirm_closure_candidate(selcx, obligation, data),
         super::VtableFnPointer(data) =>
@@ -1123,6 +1127,60 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
     confirm_param_env_candidate(selcx, obligation, env_predicate)
 }
 
+fn confirm_generator_candidate<'cx, 'gcx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>)
+    -> Progress<'tcx>
+{
+    let gen_sig = selcx.infcx().generator_sig(vtable.closure_def_id).unwrap()
+        .subst(selcx.tcx(), vtable.substs.substs);
+    let Normalized {
+        value: gen_sig,
+        obligations
+    } = normalize_with_depth(selcx,
+                             obligation.param_env,
+                             obligation.cause.clone(),
+                             obligation.recursion_depth+1,
+                             &gen_sig);
+
+    debug!("confirm_generator_candidate: obligation={:?},gen_sig={:?},obligations={:?}",
+           obligation,
+           gen_sig,
+           obligations);
+
+    let tcx = selcx.tcx();
+
+    let gen_def_id = tcx.lang_items.gen_trait().unwrap();
+
+    // Note: we unwrap the binder here but re-create it below (1)
+    let ty::Binder((trait_ref, yield_ty, return_ty)) =
+        tcx.generator_trait_ref_and_outputs(gen_def_id,
+                                            obligation.predicate.self_ty(),
+                                            gen_sig);
+
+    let name = tcx.associated_item(obligation.predicate.item_def_id).name;
+    let ty = if name == Symbol::intern("Return") {
+        return_ty
+    } else if name == Symbol::intern("Yield") {
+        yield_ty
+    } else {
+        bug!()
+    };
+
+    let predicate = ty::Binder(ty::ProjectionPredicate { // (1) recreate binder here
+        projection_ty: ty::ProjectionTy {
+            substs: trait_ref.substs,
+            item_def_id: obligation.predicate.item_def_id,
+        },
+        ty: ty
+    });
+
+    confirm_param_env_candidate(selcx, obligation, predicate)
+        .with_addl_obligations(vtable.nested)
+        .with_addl_obligations(obligations)
+}
+
 fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
index 3e39d5921351f3c05c983e4dcb6c80ebebcd2447..2e2daeb984591071f44e5ece85edacf053a008b8 100644 (file)
@@ -24,9 +24,9 @@
 use super::TraitNotObjectSafe;
 use super::Selection;
 use super::SelectionResult;
-use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
+use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableGenerator,
             VtableFnPointer, VtableObject, VtableDefaultImpl};
-use super::{VtableImplData, VtableObjectData, VtableBuiltinData,
+use super::{VtableImplData, VtableObjectData, VtableBuiltinData, VtableGeneratorData,
             VtableClosureData, VtableDefaultImplData, VtableFnPointerData};
 use super::util;
 
@@ -43,6 +43,7 @@
 
 use rustc_data_structures::bitvec::BitVector;
 use rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec};
+use std::iter;
 use std::cell::RefCell;
 use std::cmp;
 use std::fmt;
@@ -197,6 +198,10 @@ enum SelectionCandidate<'tcx> {
     /// confirmation step what ClosureKind obligation to emit.
     ClosureCandidate(/* closure */ DefId, ty::ClosureSubsts<'tcx>, ty::ClosureKind),
 
+    /// Implementation of a `Generator` trait by one of the anonymous types
+    /// generated for a generator.
+    GeneratorCandidate(/* function / closure */ DefId, ty::ClosureSubsts<'tcx>),
+
     /// Implementation of a `Fn`-family trait by one of the anonymous
     /// types generated for a fn pointer type (e.g., `fn(int)->int`)
     FnPointerCandidate,
@@ -228,6 +233,11 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
             ParamCandidate(ref trait_ref) => {
                 return tcx.lift(trait_ref).map(ParamCandidate);
             }
+            GeneratorCandidate(def_id, ref substs) => {
+                return tcx.lift(substs).map(|substs| {
+                    GeneratorCandidate(def_id, substs)
+                });
+            }
             ClosureCandidate(def_id, ref substs, kind) => {
                 return tcx.lift(substs).map(|substs| {
                     ClosureCandidate(def_id, substs, kind)
@@ -1296,6 +1306,7 @@ fn assemble_candidates<'o>(&mut self,
          } else if self.tcx().lang_items.unsize_trait() == Some(def_id) {
              self.assemble_candidates_for_unsizing(obligation, &mut candidates);
          } else {
+             self.assemble_generator_candidates(obligation, &mut candidates)?;
              self.assemble_closure_candidates(obligation, &mut candidates)?;
              self.assemble_fn_pointer_candidates(obligation, &mut candidates)?;
              self.assemble_candidates_from_impls(obligation, &mut candidates)?;
@@ -1480,6 +1491,38 @@ fn evaluate_where_clause<'o>(&mut self,
         })
     }
 
+    fn assemble_generator_candidates(&mut self,
+                                   obligation: &TraitObligation<'tcx>,
+                                   candidates: &mut SelectionCandidateSet<'tcx>)
+                                   -> Result<(),SelectionError<'tcx>>
+    {
+        if self.tcx().lang_items.gen_trait() != Some(obligation.predicate.def_id()) {
+            return Ok(());
+        }
+
+        // ok to skip binder because the substs on generator types never
+        // touch bound regions, they just capture the in-scope
+        // type/region parameters
+        let self_ty = *obligation.self_ty().skip_binder();
+        let (closure_def_id, substs) = match self_ty.sty {
+            ty::TyGenerator(id, substs, _) => (id, substs),
+            ty::TyInfer(ty::TyVar(_)) => {
+                debug!("assemble_generator_candidates: ambiguous self-type");
+                candidates.ambiguous = true;
+                return Ok(());
+            }
+            _ => { return Ok(()); }
+        };
+
+        debug!("assemble_generator_candidates: self_ty={:?} obligation={:?}",
+               self_ty,
+               obligation);
+
+        candidates.vec.push(GeneratorCandidate(closure_def_id, substs));
+
+        Ok(())
+    }
+
     /// Check for the artificial impl that the compiler will create for an obligation like `X :
     /// FnMut<..>` where `X` is a closure type.
     ///
@@ -1846,6 +1889,7 @@ fn candidate_should_be_dropped_in_favor_of<'o>(
                 }
                 ImplCandidate(..) |
                 ClosureCandidate(..) |
+                GeneratorCandidate(..) |
                 FnPointerCandidate |
                 BuiltinObjectCandidate |
                 BuiltinUnsizeCandidate |
@@ -1926,7 +1970,7 @@ fn sized_conditions(&mut self, obligation: &TraitObligation<'tcx>)
             ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
             ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
             ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) |
-            ty::TyChar | ty::TyRef(..) |
+            ty::TyChar | ty::TyRef(..) | ty::TyGenerator(..) |
             ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever |
             ty::TyError => {
                 // safe for everything
@@ -1978,7 +2022,7 @@ fn copy_conditions(&mut self, obligation: &TraitObligation<'tcx>)
             }
 
             ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
-            ty::TyClosure(..) |
+            ty::TyClosure(..) | ty::TyGenerator(..) |
             ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
                 Never
             }
@@ -2079,6 +2123,11 @@ fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec<Ty<'tcx>> {
                 substs.upvar_tys(def_id, self.tcx()).collect()
             }
 
+            ty::TyGenerator(def_id, ref substs, interior) => {
+                let witness = iter::once(interior.witness);
+                substs.upvar_tys(def_id, self.tcx()).chain(witness).collect()
+            }
+
             // for `PhantomData<T>`, we pass `T`
             ty::TyAdt(def, substs) if def.is_phantom_data() => {
                 substs.types().collect()
@@ -2188,6 +2237,12 @@ fn confirm_candidate(&mut self,
                 Ok(VtableClosure(vtable_closure))
             }
 
+            GeneratorCandidate(closure_def_id, substs) => {
+                let vtable_generator =
+                    self.confirm_generator_candidate(obligation, closure_def_id, substs)?;
+                Ok(VtableGenerator(vtable_generator))
+            }
+
             BuiltinObjectCandidate => {
                 // This indicates something like `(Trait+Send) :
                 // Send`. In this case, we know that this holds
@@ -2516,6 +2571,40 @@ fn confirm_fn_pointer_candidate(&mut self, obligation: &TraitObligation<'tcx>)
         Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations })
     }
 
+    fn confirm_generator_candidate(&mut self,
+                                 obligation: &TraitObligation<'tcx>,
+                                 closure_def_id: DefId,
+                                 substs: ty::ClosureSubsts<'tcx>)
+                                 -> Result<VtableGeneratorData<'tcx, PredicateObligation<'tcx>>,
+                                           SelectionError<'tcx>>
+    {
+        debug!("confirm_generator_candidate({:?},{:?},{:?})",
+               obligation,
+               closure_def_id,
+               substs);
+
+        let Normalized {
+            value: trait_ref,
+            obligations
+        } = self.generator_trait_ref(obligation, closure_def_id, substs);
+
+        debug!("confirm_generator_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})",
+               closure_def_id,
+               trait_ref,
+               obligations);
+
+        self.confirm_poly_trait_refs(obligation.cause.clone(),
+                                     obligation.param_env,
+                                     obligation.predicate.to_poly_trait_ref(),
+                                     trait_ref)?;
+
+        Ok(VtableGeneratorData {
+            closure_def_id: closure_def_id,
+            substs: substs.clone(),
+            nested: obligations
+        })
+    }
+
     fn confirm_closure_candidate(&mut self,
                                  obligation: &TraitObligation<'tcx>,
                                  closure_def_id: DefId,
@@ -3017,6 +3106,45 @@ fn closure_trait_ref(&mut self,
                              &trait_ref)
     }
 
+    fn generator_trait_ref_unnormalized(&mut self,
+                                      obligation: &TraitObligation<'tcx>,
+                                      closure_def_id: DefId,
+                                      substs: ty::ClosureSubsts<'tcx>)
+                                      -> ty::PolyTraitRef<'tcx>
+    {
+        let gen_sig = self.infcx.generator_sig(closure_def_id).unwrap()
+            .subst(self.tcx(), substs.substs);
+        let ty::Binder((trait_ref, ..)) =
+            self.tcx().generator_trait_ref_and_outputs(obligation.predicate.def_id(),
+                                                       obligation.predicate.0.self_ty(), // (1)
+                                                       gen_sig);
+        // (1) Feels icky to skip the binder here, but OTOH we know
+        // that the self-type is an generator type and hence is
+        // in fact unparameterized (or at least does not reference any
+        // regions bound in the obligation). Still probably some
+        // refactoring could make this nicer.
+
+        ty::Binder(trait_ref)
+    }
+
+    fn generator_trait_ref(&mut self,
+                         obligation: &TraitObligation<'tcx>,
+                         closure_def_id: DefId,
+                         substs: ty::ClosureSubsts<'tcx>)
+                         -> Normalized<'tcx, ty::PolyTraitRef<'tcx>>
+    {
+        let trait_ref = self.generator_trait_ref_unnormalized(
+            obligation, closure_def_id, substs);
+
+        // A generator signature can contain associated types which
+        // must be normalized.
+        normalize_with_depth(self,
+                             obligation.param_env,
+                             obligation.cause.clone(),
+                             obligation.recursion_depth+1,
+                             &trait_ref)
+    }
+
     /// Returns the obligations that are implied by instantiating an
     /// impl or trait. The obligations are substituted and fully
     /// normalized. This is used when confirming an impl or default
index f1c176561ea4875d908cc67689c2fc4166d02308..93994329178e75b9715348fbcbefc769c05ab36a 100644 (file)
@@ -53,6 +53,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
             super::VtableClosure(ref d) =>
                 write!(f, "{:?}", d),
 
+            super::VtableGenerator(ref d) =>
+                write!(f, "{:?}", d),
+
             super::VtableFnPointer(ref d) =>
                 write!(f, "VtableFnPointer({:?})", d),
 
@@ -77,6 +80,15 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableGeneratorData<'tcx, N> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "VtableGenerator(closure_def_id={:?}, substs={:?}, nested={:?})",
+               self.closure_def_id,
+               self.substs,
+               self.nested)
+    }
+}
+
 impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableClosureData<'tcx, N> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "VtableClosure(closure_def_id={:?}, substs={:?}, nested={:?})",
@@ -278,6 +290,19 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
                 })
             }
             traits::VtableDefaultImpl(t) => Some(traits::VtableDefaultImpl(t)),
+            traits::VtableGenerator(traits::VtableGeneratorData {
+                closure_def_id,
+                substs,
+                nested
+            }) => {
+                tcx.lift(&substs).map(|substs| {
+                    traits::VtableGenerator(traits::VtableGeneratorData {
+                        closure_def_id: closure_def_id,
+                        substs: substs,
+                        nested: nested
+                    })
+                })
+            }
             traits::VtableClosure(traits::VtableClosureData {
                 closure_def_id,
                 substs,
@@ -351,6 +376,20 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
     }
 }
 
+impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableGeneratorData<'tcx, N> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        traits::VtableGeneratorData {
+            closure_def_id: self.closure_def_id,
+            substs: self.substs.fold_with(folder),
+            nested: self.nested.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
+    }
+}
+
 impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         traits::VtableClosureData {
@@ -422,6 +461,9 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
         match *self {
             traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)),
             traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)),
+            traits::VtableGenerator(ref d) => {
+                traits::VtableGenerator(d.fold_with(folder))
+            }
             traits::VtableClosure(ref d) => {
                 traits::VtableClosure(d.fold_with(folder))
             }
@@ -438,6 +480,7 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
         match *self {
             traits::VtableImpl(ref v) => v.visit_with(visitor),
             traits::VtableDefaultImpl(ref t) => t.visit_with(visitor),
+            traits::VtableGenerator(ref d) => d.visit_with(visitor),
             traits::VtableClosure(ref d) => d.visit_with(visitor),
             traits::VtableFnPointer(ref d) => d.visit_with(visitor),
             traits::VtableParam(ref n) => n.visit_with(visitor),
index dae0c896909c85cd69d161af8e562db93168b589..28abd1577dadea39ff7b126f582a3a0329a2a031 100644 (file)
@@ -513,6 +513,19 @@ pub fn closure_trait_ref_and_return_type(self,
         ty::Binder((trait_ref, sig.skip_binder().output()))
     }
 
+    pub fn generator_trait_ref_and_outputs(self,
+        fn_trait_def_id: DefId,
+        self_ty: Ty<'tcx>,
+        sig: ty::PolyGenSig<'tcx>)
+        -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)>
+    {
+        let trait_ref = ty::TraitRef {
+            def_id: fn_trait_def_id,
+            substs: self.mk_substs_trait(self_ty, &[]),
+        };
+        ty::Binder((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
+    }
+
     pub fn impl_is_default(self, node_item_def_id: DefId) -> bool {
         match self.hir.as_local_node_id(node_item_def_id) {
             Some(node_id) => {
index 6b9cbabf20e977887e59021f15c8b52f0b9775d7..c2c52e5fa21b6be2eb28e0a107190cb52e5c3612 100644 (file)
@@ -31,7 +31,7 @@
 use traits;
 use ty::{self, Ty, TypeAndMut};
 use ty::{TyS, TypeVariants, Slice};
-use ty::{AdtKind, AdtDef, ClosureSubsts, Region};
+use ty::{AdtKind, AdtDef, ClosureSubsts, GeneratorInterior, Region};
 use hir::FreevarMap;
 use ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predicate};
 use ty::RegionKind;
@@ -239,6 +239,10 @@ pub struct TypeckTables<'tcx> {
     /// that caused the closure to be this kind.
     pub closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>,
 
+    pub generator_sigs: NodeMap<Option<ty::GenSig<'tcx>>>,
+
+    pub generator_interiors: NodeMap<ty::GeneratorInterior<'tcx>>,
+
     /// For each fn, records the "liberated" types of its arguments
     /// and return type. Liberated means that all bound regions
     /// (including late-bound regions) are replaced with free
@@ -279,6 +283,8 @@ pub fn empty() -> TypeckTables<'tcx> {
             adjustments: NodeMap(),
             pat_binding_modes: NodeMap(),
             upvar_capture_map: FxHashMap(),
+            generator_sigs: NodeMap(),
+            generator_interiors: NodeMap(),
             closure_tys: NodeMap(),
             closure_kinds: NodeMap(),
             liberated_fn_sigs: NodeMap(),
@@ -1043,7 +1049,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
     pub fn print_debug_stats(self) {
         sty_debug_print!(
             self,
-            TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr,
+            TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyGenerator,
             TyDynamic, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon);
 
         println!("Substs interner: #{}", self.interners.substs.borrow().len());
@@ -1398,6 +1404,14 @@ pub fn mk_closure_from_closure_substs(self,
         self.mk_ty(TyClosure(closure_id, closure_substs))
     }
 
+    pub fn mk_generator(self,
+                        id: DefId,
+                        closure_substs: ClosureSubsts<'tcx>,
+                        interior: GeneratorInterior<'tcx>)
+                        -> Ty<'tcx> {
+        self.mk_ty(TyGenerator(id, closure_substs, interior))
+    }
+
     pub fn mk_var(self, v: TyVid) -> Ty<'tcx> {
         self.mk_infer(TyVar(v))
     }
index 86a4f66918965f3df58cd4a229c202501df477f0..802994ae0948ab452c89afbc75cf6dfff9177315 100644 (file)
@@ -213,6 +213,7 @@ pub fn sort_string(&self, tcx: TyCtxt<'a, 'gcx, 'lcx>) -> String {
                     |p| format!("trait {}", tcx.item_path_str(p.def_id())))
             }
             ty::TyClosure(..) => "closure".to_string(),
+            ty::TyGenerator(..) => "generator".to_string(),
             ty::TyTuple(..) => "tuple".to_string(),
             ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(),
             ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(),
index 68f85ba7d33e23dc87beb3ab93134b71352c6edd..353a1cd5355b9b14ca427f16fc65e5930e09bb3b 100644 (file)
@@ -30,6 +30,7 @@ pub enum SimplifiedType {
     TupleSimplifiedType(usize),
     TraitSimplifiedType(DefId),
     ClosureSimplifiedType(DefId),
+    GeneratorSimplifiedType(DefId),
     AnonSimplifiedType(DefId),
     FunctionSimplifiedType(usize),
     ParameterSimplifiedType,
@@ -72,6 +73,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         ty::TyClosure(def_id, _) => {
             Some(ClosureSimplifiedType(def_id))
         }
+        ty::TyGenerator(def_id, _, _) => {
+            Some(GeneratorSimplifiedType(def_id))
+        }
         ty::TyNever => Some(NeverSimplifiedType),
         ty::TyTuple(ref tys, _) => {
             Some(TupleSimplifiedType(tys.len()))
index ce2bb23660ce09f13eadc47cbb61eb795ecf4bd2..f40e1d370a99504ab0aec92780d284fd6d72d164 100644 (file)
@@ -85,6 +85,13 @@ fn add_sty(&mut self, st: &ty::TypeVariants) {
                 }
             }
 
+            &ty::TyGenerator(_, ref substs, ref interior) => {
+                self.add_flags(TypeFlags::HAS_TY_CLOSURE);
+                self.add_flags(TypeFlags::HAS_LOCAL_NAMES);
+                self.add_substs(&substs.substs);
+                self.add_ty(interior.witness);
+            }
+
             &ty::TyClosure(_, ref substs) => {
                 self.add_flags(TypeFlags::HAS_TY_CLOSURE);
                 self.add_flags(TypeFlags::HAS_LOCAL_NAMES);
index 73b577e2e876b776b37c1da8f0ccb942c54bd81e..01640712a38b8259aa461776dfd774b773552d9b 100644 (file)
@@ -350,6 +350,7 @@ pub fn characteristic_def_id_of_type(ty: Ty) -> Option<DefId> {
 
         ty::TyFnDef(def_id, _) |
         ty::TyClosure(def_id, _) => Some(def_id),
+        ty::TyGenerator(def_id, _, _) => Some(def_id),
 
         ty::TyBool |
         ty::TyChar |
index 4ee9b2e65a782ac79c88ad3062caaeb6e7cb8df3..cf21a66d51538f9d66147b9185783030d17e7cfe 100644 (file)
@@ -1226,7 +1226,17 @@ pub fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 Univariant { variant: unit, non_zero: false }
             }
 
-            // Tuples and closures.
+            // Tuples, generators and closures.
+            ty::TyGenerator(def_id, ref substs, _) => {
+                let tys = substs.field_tys(def_id, tcx);
+                let st = Struct::new(dl,
+                    &tys.map(|ty| ty.layout(tcx, param_env))
+                      .collect::<Result<Vec<_>, _>>()?,
+                    &ReprOptions::default(),
+                    StructKind::AlwaysSizedUnivariant, ty)?;
+                Univariant { variant: st, non_zero: false }
+            }
+
             ty::TyClosure(def_id, ref substs) => {
                 let tys = substs.upvar_tys(def_id, tcx);
                 let st = Struct::new(dl,
@@ -2240,11 +2250,15 @@ pub fn field_type<C: LayoutTyper<'tcx>>(&self, cx: C, i: usize) -> Ty<'tcx> {
             ty::TySlice(element) => element,
             ty::TyStr => tcx.types.u8,
 
-            // Tuples and closures.
+            // Tuples, generators and closures.
             ty::TyClosure(def_id, ref substs) => {
                 substs.upvar_tys(def_id, tcx).nth(i).unwrap()
             }
 
+            ty::TyGenerator(def_id, ref substs, _) => {
+                substs.field_tys(def_id, tcx).nth(i).unwrap()
+            }
+
             ty::TyTuple(tys, _) => tys[i],
 
             // SIMD vector types.
index b871b36c948500100ab7f118a632d7dc6fa6a71e..b121c05f244fdd2ae8a53e5515dfb161d3a4c365 100644 (file)
@@ -541,8 +541,15 @@ pub enum Query<$tcx> {
 
         impl<$tcx> Query<$tcx> {
             pub fn describe(&self, tcx: TyCtxt) -> String {
-                match *self {
-                    $(Query::$name(key) => queries::$name::describe(tcx, key)),*
+                let (r, name) = match *self {
+                    $(Query::$name(key) => {
+                        (queries::$name::describe(tcx, key), stringify!($name))
+                    })*
+                };
+                if tcx.sess.verbose() {
+                    format!("{} [{}]", r, name)
+                } else {
+                    r
                 }
             }
         }
@@ -910,6 +917,10 @@ fn default() -> Self {
     /// The signature of functions and closures.
     [] fn_sig: FnSignature(DefId) -> ty::PolyFnSig<'tcx>,
 
+    /// Records the signature of each generator. The def ID is the ID of the
+    /// expression defining the closure.
+    [] generator_sig: TypeckTables(DefId) -> Option<ty::PolyGenSig<'tcx>>,
+
     /// Caches CoerceUnsized kinds for impls on custom types.
     [] coerce_unsized_info: CoerceUnsizedInfo(DefId)
         -> ty::adjustment::CoerceUnsizedInfo,
index 28a73f4a4d387dcbe36170619619c236cd78baa0..0a3c4d5d8fa5a4114fc345e3204545dfcf1d6494 100644 (file)
@@ -25,6 +25,7 @@
 use middle::resolve_lifetime::ObjectLifetimeDefault;
 use middle::region::CodeExtent;
 use mir::Mir;
+use mir::GeneratorLayout;
 use traits;
 use ty;
 use ty::subst::{Subst, Substs};
@@ -59,9 +60,9 @@
 use hir;
 
 pub use self::sty::{Binder, DebruijnIndex};
-pub use self::sty::{FnSig, PolyFnSig};
+pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
-pub use self::sty::{ClosureSubsts, TypeAndMut};
+pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut};
 pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
 pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef};
 pub use self::sty::{ExistentialProjection, PolyExistentialProjection};
@@ -408,6 +409,8 @@ pub struct CReaderCacheKey {
         const HAS_FREE_REGIONS   = 1 << 6,
         const HAS_TY_ERR         = 1 << 7,
         const HAS_PROJECTION     = 1 << 8,
+
+        // FIXME: Rename this to the actual property since it's used for generators too
         const HAS_TY_CLOSURE     = 1 << 9,
 
         // true if there are "names" of types and regions and so forth
@@ -1702,7 +1705,7 @@ fn sized_constraint_for_ty(&self,
         let result = match ty.sty {
             TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
             TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
-            TyArray(..) | TyClosure(..) | TyNever => {
+            TyArray(..) | TyClosure(..) | TyGenerator(..) | TyNever => {
                 vec![]
             }
 
@@ -2030,6 +2033,7 @@ pub fn expr_is_lval(self, expr: &hir::Expr) -> bool {
             hir::ExprBox(..) |
             hir::ExprAddrOf(..) |
             hir::ExprBinary(..) |
+            hir::ExprYield(..) |
             hir::ExprCast(..) => {
                 false
             }
@@ -2261,6 +2265,10 @@ pub fn trait_has_default_impl(self, trait_def_id: DefId) -> bool {
         self.trait_def(trait_def_id).has_default_impl
     }
 
+    pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> {
+        self.optimized_mir(def_id).generator_layout.as_ref().unwrap()
+    }
+
     /// Given the def_id of an impl, return the def_id of the trait it implements.
     /// If it implements no trait, return `None`.
     pub fn trait_id_of_impl(self, def_id: DefId) -> Option<DefId> {
index ab1b1b3857d0035c81695027d48a439a2cc86813..657ed4077911c8cd0dd1ea353cce5b366c230fe6 100644 (file)
@@ -115,6 +115,16 @@ fn compute_components(&self, ty: Ty<'tcx>, out: &mut Vec<Component<'tcx>>) {
                 }
             }
 
+            ty::TyGenerator(def_id, ref substs, ref interior) => {
+                // Same as the closure case
+                for upvar_ty in substs.upvar_tys(def_id, *self) {
+                    self.compute_components(upvar_ty, out);
+                }
+
+                // But generators can have additional interior types
+                self.compute_components(interior.witness, out);
+            }
+
             // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
             // is implied by the environment is done in regionck.
             ty::TyParam(p) => {
index 0d9ef8196c79451aee01ff55727221a8b043a57e..da94eddf295ea8c4b2eb2a9b75735e3339b1b879 100644 (file)
@@ -389,6 +389,18 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
             Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound))
         }
 
+        (&ty::TyGenerator(a_id, a_substs, a_interior),
+         &ty::TyGenerator(b_id, b_substs, b_interior))
+            if a_id == b_id =>
+        {
+            // All TyGenerator types with the same id represent
+            // the (anonymous) type of the same generator expression. So
+            // all of their regions should be equated.
+            let substs = relation.relate(&a_substs, &b_substs)?;
+            let interior = relation.relate(&a_interior, &b_interior)?;
+            Ok(tcx.mk_generator(a_id, substs, interior))
+        }
+
         (&ty::TyClosure(a_id, a_substs),
          &ty::TyClosure(b_id, b_substs))
             if a_id == b_id =>
@@ -512,6 +524,18 @@ fn relate<'a, 'gcx, R>(relation: &mut R,
     }
 }
 
+impl<'tcx> Relate<'tcx> for ty::GeneratorInterior<'tcx> {
+    fn relate<'a, 'gcx, R>(relation: &mut R,
+                           a: &ty::GeneratorInterior<'tcx>,
+                           b: &ty::GeneratorInterior<'tcx>)
+                           -> RelateResult<'tcx, ty::GeneratorInterior<'tcx>>
+        where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a
+    {
+        let interior = relation.relate(&a.witness, &b.witness)?;
+        Ok(ty::GeneratorInterior::new(interior))
+    }
+}
+
 impl<'tcx> Relate<'tcx> for &'tcx Substs<'tcx> {
     fn relate<'a, 'gcx, R>(relation: &mut R,
                            a: &&'tcx Substs<'tcx>,
index 48ace804995ab238697101669dcbf644791c9eaf..cbb0a45cf1e1478c7c8b99aae98adf49c8030867 100644 (file)
@@ -29,6 +29,15 @@ fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lif
     }
 }
 
+impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) {
+    type Lifted = (A::Lifted, B::Lifted, C::Lifted);
+    fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&self.0).and_then(|a| {
+            tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))
+        })
+    }
+}
+
 impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
     type Lifted = Option<T::Lifted>;
     fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@@ -220,6 +229,15 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorInterior<'a> {
+    type Lifted = ty::GeneratorInterior<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&self.witness).map(|witness| {
+            ty::GeneratorInterior { witness }
+        })
+    }
+}
+
 impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> {
     type Lifted = ty::adjustment::Adjustment<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@@ -283,6 +301,19 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> {
+    type Lifted = ty::GenSig<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        tcx.lift(&(self.yield_ty, self.return_ty))
+            .map(|(yield_ty, return_ty)| {
+                ty::GenSig {
+                    yield_ty,
+                    return_ty,
+                }
+            })
+    }
+}
+
 impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> {
     type Lifted = ty::FnSig<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@@ -539,6 +570,9 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
             ty::TyRef(ref r, tm) => {
                 ty::TyRef(r.fold_with(folder), tm.fold_with(folder))
             }
+            ty::TyGenerator(did, substs, interior) => {
+                ty::TyGenerator(did, substs.fold_with(folder), interior.fold_with(folder))
+            }
             ty::TyClosure(did, substs) => ty::TyClosure(did, substs.fold_with(folder)),
             ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)),
             ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)),
@@ -570,6 +604,9 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
             ty::TyFnDef(_, substs) => substs.visit_with(visitor),
             ty::TyFnPtr(ref f) => f.visit_with(visitor),
             ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor),
+            ty::TyGenerator(_did, ref substs, ref interior) => {
+                substs.visit_with(visitor) || interior.visit_with(visitor)
+            }
             ty::TyClosure(_did, ref substs) => substs.visit_with(visitor),
             ty::TyProjection(ref data) => data.visit_with(visitor),
             ty::TyAnon(_, ref substs) => substs.visit_with(visitor),
@@ -594,6 +631,20 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for ty::GenSig<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        ty::GenSig {
+            yield_ty: self.yield_ty.fold_with(folder),
+            return_ty: self.return_ty.fold_with(folder),
+        }
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.yield_ty.visit_with(visitor) ||
+        self.return_ty.visit_with(visitor)
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         let inputs_and_output = self.inputs_and_output.fold_with(folder);
@@ -684,6 +735,16 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
     }
 }
 
+impl<'tcx> TypeFoldable<'tcx> for ty::GeneratorInterior<'tcx> {
+    fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+        ty::GeneratorInterior::new(self.witness.fold_with(folder))
+    }
+
+    fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+        self.witness.visit_with(visitor)
+    }
+}
+
 impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjustment<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         ty::adjustment::Adjustment {
index b42180b288bf6cb7cc43484d7293cba7ca1e529f..2a310e1d5df78aa97bbc6b786f4a5d5d4dcff045 100644 (file)
@@ -135,6 +135,10 @@ pub enum TypeVariants<'tcx> {
     /// `|a| a`.
     TyClosure(DefId, ClosureSubsts<'tcx>),
 
+    /// The anonymous type of a generator. Used to represent the type of
+    /// `|a| yield a`.
+    TyGenerator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>),
+
     /// The never type `!`
     TyNever,
 
@@ -261,6 +265,51 @@ impl Iterator<Item=Ty<'tcx>> + 'tcx
     }
 }
 
+impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> {
+    /// This returns the types of the MIR locals which had to be stored across suspension points.
+    /// It is calculated in rustc_mir::transform::generator::StateTransform.
+    /// All the types here must be in the tuple in GeneratorInterior.
+    pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
+        impl Iterator<Item=Ty<'tcx>> + 'a
+    {
+        let state = tcx.generator_layout(def_id).fields.iter();
+        state.map(move |d| d.ty.subst(tcx, self.substs))
+    }
+
+    /// This is the types of all the fields stored in a generator.
+    /// It includes the upvars, state types and the state discriminant which is u32.
+    pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
+        impl Iterator<Item=Ty<'tcx>> + 'a
+    {
+        let upvars = self.upvar_tys(def_id, tcx);
+        let state = self.state_tys(def_id, tcx);
+        upvars.chain(iter::once(tcx.types.u32)).chain(state)
+    }
+}
+
+/// This describes the types that can be contained in a generator.
+/// It will be a type variable initially and unified in the last stages of typeck of a body.
+/// It contains a tuple of all the types that could end up on a generator frame.
+/// The state transformation MIR pass may only produce layouts which mention types in this tuple.
+/// Upvars are not counted here.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
+pub struct GeneratorInterior<'tcx> {
+    pub witness: Ty<'tcx>,
+}
+
+impl<'tcx> GeneratorInterior<'tcx> {
+    pub fn new(witness: Ty<'tcx>) -> GeneratorInterior<'tcx> {
+        GeneratorInterior { witness }
+    }
+
+    pub fn as_slice(&self) -> &'tcx Slice<Ty<'tcx>> {
+        match self.witness.sty {
+            ty::TyTuple(s, _) => s,
+            _ => bug!(),
+        }
+    }
+}
+
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum ExistentialPredicate<'tcx> {
     /// e.g. Iterator
@@ -579,6 +628,22 @@ pub fn self_ty(&self) -> Ty<'tcx> {
     }
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+pub struct GenSig<'tcx> {
+    pub yield_ty: Ty<'tcx>,
+    pub return_ty: Ty<'tcx>,
+}
+
+pub type PolyGenSig<'tcx> = Binder<GenSig<'tcx>>;
+
+impl<'tcx> PolyGenSig<'tcx> {
+    pub fn yield_ty(&self) -> ty::Binder<Ty<'tcx>> {
+        self.map_bound_ref(|sig| sig.yield_ty)
+    }
+    pub fn return_ty(&self) -> ty::Binder<Ty<'tcx>> {
+        self.map_bound_ref(|sig| sig.return_ty)
+    }
+}
 
 /// Signature of a function type, which I have arbitrarily
 /// decided to use to refer to the input/output types.
@@ -1379,7 +1444,7 @@ pub fn regions(&self) -> Vec<ty::Region<'tcx>> {
             TyAdt(_, substs) | TyAnon(_, substs) => {
                 substs.regions().collect()
             }
-            TyClosure(_, ref substs) => {
+            TyClosure(_, ref substs) | TyGenerator(_, ref substs, _) => {
                 substs.substs.regions().collect()
             }
             TyProjection(ref data) => {
index 9cd6aa2111873a69f9becbb6b011e205b25f2978..82810b7aae1bb1a70b93af6f6c5399ed8c6929be 100644 (file)
@@ -27,6 +27,7 @@
                                            HashStable};
 use rustc_data_structures::fx::FxHashMap;
 use std::cmp;
+use std::iter;
 use std::hash::Hash;
 use std::intrinsics;
 use syntax::ast::{self, Name};
@@ -573,6 +574,12 @@ pub fn dtorck_constraint_for_ty(self,
                 }).collect()
             }
 
+            ty::TyGenerator(def_id, substs, interior) => {
+                substs.upvar_tys(def_id, self).chain(iter::once(interior.witness)).map(|ty| {
+                    self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
+                }).collect()
+            }
+
             ty::TyAdt(def, substs) => {
                 let ty::DtorckConstraint {
                     dtorck_types, outlives
@@ -694,6 +701,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
             TyRawPtr(m) |
             TyRef(_, m) => self.hash(m.mutbl),
             TyClosure(def_id, _) |
+            TyGenerator(def_id, _, _) |
             TyAnon(def_id, _) |
             TyFnDef(def_id, _) => self.def_id(def_id),
             TyAdt(d, _) => self.def_id(d.did),
@@ -1116,6 +1124,8 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         ty::TyClosure(def_id, ref substs) => substs.upvar_tys(def_id, tcx).any(needs_drop),
 
+        ty::TyGenerator(..) => true,
+
         ty::TyTuple(ref tys, _) => tys.iter().cloned().any(needs_drop),
 
         // unions don't have destructors regardless of the child types
index a7f0bafe9b67d04ba78335430dcddf3843d24ece..bfabacdb1721496be8eb7bbcd18ff84b5312ac6d 100644 (file)
@@ -112,6 +112,10 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
         ty::TyClosure(_, ref substs) => {
             stack.extend(substs.substs.types().rev());
         }
+        ty::TyGenerator(_, ref substs, ref interior) => {
+            stack.extend(substs.substs.types().rev());
+            stack.push(interior.witness);
+        }
         ty::TyTuple(ts, _) => {
             stack.extend(ts.iter().cloned().rev());
         }
index c24c583ad1e19a384056197b4f1fe579040562c8..e2abd16d001bcdae344aa290fc1c97f5345c1db0 100644 (file)
@@ -239,8 +239,8 @@ fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
                     }
                 }
 
-                ty::TyClosure(..) => {
-                    // the types in a closure are always the types of
+                ty::TyGenerator(..) | ty::TyClosure(..) => {
+                    // the types in a closure or generator are always the types of
                     // local variables (or possibly references to local
                     // variables), we'll walk those.
                     //
index d9c99ccd50843d79e6e4bcadc974f771de3d2a4a..ed72f2429487f49a739342072b39dba970bf675b 100644 (file)
@@ -17,7 +17,7 @@
 use ty::{TyBool, TyChar, TyAdt};
 use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
 use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple};
-use ty::{TyClosure, TyProjection, TyAnon};
+use ty::{TyClosure, TyGenerator, TyProjection, TyAnon};
 use ty::{TyDynamic, TyInt, TyUint, TyInfer};
 use ty::{self, Ty, TyCtxt, TypeFoldable};
 
@@ -715,6 +715,12 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+impl<'tcx> fmt::Display for ty::GeneratorInterior<'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.witness.fmt(f)
+    }
+}
+
 impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -813,6 +819,41 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                 })
             }
             TyStr => write!(f, "str"),
+            TyGenerator(did, substs, interior) => ty::tls::with(|tcx| {
+                let upvar_tys = substs.upvar_tys(did, tcx);
+                write!(f, "[generator")?;
+
+                if let Some(node_id) = tcx.hir.as_local_node_id(did) {
+                    write!(f, "@{:?}", tcx.hir.span(node_id))?;
+                    let mut sep = " ";
+                    tcx.with_freevars(node_id, |freevars| {
+                        for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) {
+                            let def_id = freevar.def.def_id();
+                            let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
+                            write!(f,
+                                        "{}{}:{}",
+                                        sep,
+                                        tcx.local_var_name_str(node_id),
+                                        upvar_ty)?;
+                            sep = ", ";
+                        }
+                        Ok(())
+                    })?
+                } else {
+                    // cross-crate closure types should only be
+                    // visible in trans bug reports, I imagine.
+                    write!(f, "@{:?}", did)?;
+                    let mut sep = " ";
+                    for (index, upvar_ty) in upvar_tys.enumerate() {
+                        write!(f, "{}{}:{}", sep, index, upvar_ty)?;
+                        sep = ", ";
+                    }
+                }
+
+                write!(f, " {}", interior)?;
+
+                write!(f, "]")
+            }),
             TyClosure(did, substs) => ty::tls::with(|tcx| {
                 let upvar_tys = substs.upvar_tys(did, tcx);
                 write!(f, "[closure")?;
index 3d669aa81df6e7bce6aa2bcdffcbe400b6613e55..ec88bcf9c79c348713a5928d78ea36c3b0caa6d5 100644 (file)
@@ -48,9 +48,8 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
         move_error_collector: move_error::MoveErrorCollector::new(),
     };
 
-    let body = glcx.bccx.tcx.hir.body(body);
     euv::ExprUseVisitor::new(&mut glcx, bccx.tcx, param_env, &bccx.region_maps, bccx.tables)
-        .consume_body(body);
+        .consume_body(bccx.body);
 
     glcx.report_potential_errors();
     let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
@@ -133,7 +132,6 @@ fn borrow(&mut self,
                bk={:?}, loan_cause={:?})",
                borrow_id, cmt, loan_region,
                bk, loan_cause);
-
         self.guarantee_valid(borrow_id,
                              borrow_span,
                              cmt,
index 6b31535c5a5260592066d33039388692fce50cd3..ca047b0d488abe16fb063826c015f6063ae543f5 100644 (file)
@@ -36,7 +36,7 @@
 use rustc::middle::free_region::RegionRelations;
 use rustc::ty::{self, TyCtxt};
 use rustc::ty::maps::Providers;
-
+use rustc::util::nodemap::FxHashMap;
 use std::fmt;
 use std::rc::Rc;
 use std::hash::{Hash, Hasher};
@@ -98,9 +98,8 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
     let body_id = tcx.hir.body_owned_by(owner_id);
     let tables = tcx.typeck_tables_of(owner_def_id);
     let region_maps = tcx.region_maps(owner_def_id);
-    let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
-
-    let body = bccx.tcx.hir.body(body_id);
+    let body = tcx.hir.body(body_id);
+    let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
 
     // Eventually, borrowck will always read the MIR, but at the
     // moment we do not. So, for now, we always force MIR to be
@@ -137,10 +136,9 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
 {
     // Check the body of fn items.
     let tcx = this.tcx;
-    let body = tcx.hir.body(body_id);
     let id_range = {
         let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.hir);
-        visitor.visit_body(body);
+        visitor.visit_body(this.body);
         visitor.result()
     };
     let (all_loans, move_data) =
@@ -161,7 +159,7 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
     let mut loan_dfcx =
         DataFlowContext::new(this.tcx,
                              "borrowck",
-                             Some(body),
+                             Some(this.body),
                              cfg,
                              LoanDataFlowOperator,
                              id_range,
@@ -172,13 +170,13 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
                            loan.kill_scope.node_id(), loan_idx);
     }
     loan_dfcx.add_kills_from_flow_exits(cfg);
-    loan_dfcx.propagate(cfg, body);
+    loan_dfcx.propagate(cfg, this.body);
 
     let flowed_moves = move_data::FlowedMoveData::new(move_data,
                                                       this,
                                                       cfg,
                                                       id_range,
-                                                      body);
+                                                      this.body);
 
     Some(AnalysisData { all_loans: all_loans,
                         loans: loan_dfcx,
@@ -197,7 +195,8 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
     let owner_def_id = tcx.hir.local_def_id(owner_id);
     let tables = tcx.typeck_tables_of(owner_def_id);
     let region_maps = tcx.region_maps(owner_def_id);
-    let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
+    let body = tcx.hir.body(body_id);
+    let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
 
     let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
     (bccx, dataflow_data.unwrap())
@@ -216,6 +215,8 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
     region_maps: Rc<RegionMaps>,
 
     owner_def_id: DefId,
+
+    body: &'tcx hir::Body,
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -327,7 +328,7 @@ pub fn closure_to_block(closure_id: ast::NodeId,
                         tcx: TyCtxt) -> ast::NodeId {
     match tcx.hir.get(closure_id) {
         hir_map::NodeExpr(expr) => match expr.node {
-            hir::ExprClosure(.., body_id, _) => {
+            hir::ExprClosure(.., body_id, _, _) => {
                 body_id.node_id
             }
             _ => {
@@ -527,9 +528,7 @@ pub fn report(&self, err: BckError<'tcx>) {
             _ => { }
         }
 
-        let mut db = self.bckerr_to_diag(&err);
-        self.note_and_explain_bckerr(&mut db, err);
-        db.emit();
+        self.report_bckerr(&err);
     }
 
     pub fn report_use_of_moved_value(&self,
@@ -630,7 +629,7 @@ pub fn report_use_of_moved_value(&self,
 
             move_data::Captured =>
                 (match self.tcx.hir.expect_expr(the_move.id).node {
-                    hir::ExprClosure(.., fn_decl_span) => fn_decl_span,
+                    hir::ExprClosure(.., fn_decl_span, _) => fn_decl_span,
                     ref r => bug!("Captured({}) maps to non-closure: {:?}",
                                   the_move.id, r),
                 }, " (into closure)"),
@@ -714,8 +713,8 @@ pub fn span_err_with_code<S: Into<MultiSpan>>(&self, s: S, msg: &str, code: &str
         self.tcx.sess.span_err_with_code(s, msg, code);
     }
 
-    fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
-        let span = err.span.clone();
+    fn report_bckerr(&self, err: &BckError<'tcx>) {
+        let error_span = err.span.clone();
 
         match err.code {
             err_mutbl => {
@@ -739,12 +738,16 @@ fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
                     }
                 };
 
-                match err.cause {
+                let mut db = match err.cause {
                     MutabilityViolation => {
-                        struct_span_err!(self.tcx.sess, span, E0594, "cannot assign to {}", descr)
+                        struct_span_err!(self.tcx.sess,
+                                         error_span,
+                                         E0594,
+                                         "cannot assign to {}",
+                                         descr)
                     }
                     BorrowViolation(euv::ClosureCapture(_)) => {
-                        struct_span_err!(self.tcx.sess, span, E0595,
+                        struct_span_err!(self.tcx.sess, error_span, E0595,
                                          "closure cannot assign to {}", descr)
                     }
                     BorrowViolation(euv::OverloadedOperator) |
@@ -754,30 +757,228 @@ fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
                     BorrowViolation(euv::AutoUnsafe) |
                     BorrowViolation(euv::ForLoop) |
                     BorrowViolation(euv::MatchDiscriminant) => {
-                        struct_span_err!(self.tcx.sess, span, E0596,
+                        struct_span_err!(self.tcx.sess, error_span, E0596,
                                          "cannot borrow {} as mutable", descr)
                     }
                     BorrowViolation(euv::ClosureInvocation) => {
                         span_bug!(err.span,
                             "err_mutbl with a closure invocation");
                     }
-                }
+                };
+
+                self.note_and_explain_mutbl_error(&mut db, &err, &error_span);
+                self.note_immutability_blame(&mut db, err.cmt.immutability_blame());
+                db.emit();
             }
-            err_out_of_scope(..) => {
+            err_out_of_scope(super_scope, sub_scope, cause) => {
                 let msg = match opt_loan_path(&err.cmt) {
                     None => "borrowed value".to_string(),
                     Some(lp) => {
                         format!("`{}`", self.loan_path_to_string(&lp))
                     }
                 };
-                struct_span_err!(self.tcx.sess, span, E0597, "{} does not live long enough", msg)
+
+                // When you have a borrow that lives across a yield,
+                // that reference winds up captured in the generator
+                // type. Regionck then constraints it to live as long
+                // as the generator itself. If that borrow is borrowing
+                // data owned by the generator, this winds up resulting in
+                // an `err_out_of_scope` error:
+                //
+                // ```
+                // {
+                //     let g = || {
+                //         let a = &3; // this borrow is forced to ... -+
+                //         yield ();          //                        |
+                //         println!("{}", a); //                        |
+                //     };                     //                        |
+                // } <----------------------... live until here --------+
+                // ```
+                //
+                // To detect this case, we look for cases where the
+                // `super_scope` (lifetime of the value) is within the
+                // body, but the `sub_scope` is not.
+                debug!("err_out_of_scope: self.body.is_generator = {:?}",
+                       self.body.is_generator);
+                let maybe_borrow_across_yield = if self.body.is_generator {
+                    let body_extent = region::CodeExtent::Misc(self.body.id().node_id);
+                    debug!("err_out_of_scope: body_extent = {:?}", body_extent);
+                    debug!("err_out_of_scope: super_scope = {:?}", super_scope);
+                    debug!("err_out_of_scope: sub_scope = {:?}", sub_scope);
+                    match (super_scope, sub_scope) {
+                        (&ty::RegionKind::ReScope(value_extent),
+                         &ty::RegionKind::ReScope(loan_extent)) => {
+                            if {
+                                // value_extent <= body_extent &&
+                                self.region_maps.is_subscope_of(value_extent, body_extent) &&
+                                    // body_extent <= loan_extent
+                                    self.region_maps.is_subscope_of(body_extent, loan_extent)
+                            } {
+                                // We now know that this is a case
+                                // that fits the bill described above:
+                                // a borrow of something whose scope
+                                // is within the generator, but the
+                                // borrow is for a scope outside the
+                                // generator.
+                                //
+                                // Now look within the scope of the of
+                                // the value being borrowed (in the
+                                // example above, that would be the
+                                // block remainder that starts with
+                                // `let a`) for a yield. We can cite
+                                // that for the user.
+                                self.tcx.yield_in_extent(value_extent, &mut FxHashMap())
+                            } else {
+                                None
+                            }
+                        }
+                        _ => None,
+                    }
+                } else {
+                    None
+                };
+
+                if let Some(yield_span) = maybe_borrow_across_yield {
+                    debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span);
+                    struct_span_err!(self.tcx.sess,
+                                     error_span,
+                                     E0626,
+                                     "borrow may still be in use when generator yields")
+                        .span_label(yield_span, "possible yield occurs here")
+                        .emit();
+                    return;
+                }
+
+                let mut db = struct_span_err!(self.tcx.sess,
+                                              error_span,
+                                              E0597,
+                                              "{} does not live long enough",
+                                              msg);
+
+                let (value_kind, value_msg) = match err.cmt.cat {
+                    mc::Categorization::Rvalue(..) =>
+                        ("temporary value", "temporary value created here"),
+                    _ =>
+                        ("borrowed value", "borrow occurs here")
+                };
+
+                let is_closure = match cause {
+                    euv::ClosureCapture(s) => {
+                        // The primary span starts out as the closure creation point.
+                        // Change the primary span here to highlight the use of the variable
+                        // in the closure, because it seems more natural. Highlight
+                        // closure creation point as a secondary span.
+                        match db.span.primary_span() {
+                            Some(primary) => {
+                                db.span = MultiSpan::from_span(s);
+                                db.span_label(primary, "capture occurs here");
+                                db.span_label(s, "does not live long enough");
+                                true
+                            }
+                            None => false
+                        }
+                    }
+                    _ => {
+                        db.span_label(error_span, "does not live long enough");
+                        false
+                    }
+                };
+
+                let sub_span = self.region_end_span(sub_scope);
+                let super_span = self.region_end_span(super_scope);
+
+                match (sub_span, super_span) {
+                    (Some(s1), Some(s2)) if s1 == s2 => {
+                        if !is_closure {
+                            db.span = MultiSpan::from_span(s1);
+                            db.span_label(error_span, value_msg);
+                            let msg = match opt_loan_path(&err.cmt) {
+                                None => value_kind.to_string(),
+                                Some(lp) => {
+                                    format!("`{}`", self.loan_path_to_string(&lp))
+                                }
+                            };
+                            db.span_label(s1,
+                                          format!("{} dropped here while still borrowed", msg));
+                        } else {
+                            db.span_label(s1, format!("{} dropped before borrower", value_kind));
+                        }
+                        db.note("values in a scope are dropped in the opposite order \
+                                they are created");
+                    }
+                    (Some(s1), Some(s2)) if !is_closure => {
+                        db.span = MultiSpan::from_span(s2);
+                        db.span_label(error_span, value_msg);
+                        let msg = match opt_loan_path(&err.cmt) {
+                            None => value_kind.to_string(),
+                            Some(lp) => {
+                                format!("`{}`", self.loan_path_to_string(&lp))
+                            }
+                        };
+                        db.span_label(s2, format!("{} dropped here while still borrowed", msg));
+                        db.span_label(s1, format!("{} needs to live until here", value_kind));
+                    }
+                    _ => {
+                        match sub_span {
+                            Some(s) => {
+                                db.span_label(s, format!("{} needs to live until here",
+                                                          value_kind));
+                            }
+                            None => {
+                                self.tcx.note_and_explain_region(
+                                    &mut db,
+                                    "borrowed value must be valid for ",
+                                    sub_scope,
+                                    "...");
+                            }
+                        }
+                        match super_span {
+                            Some(s) => {
+                                db.span_label(s, format!("{} only lives until here", value_kind));
+                            }
+                            None => {
+                                self.tcx.note_and_explain_region(
+                                    &mut db,
+                                    "...but borrowed value is only valid for ",
+                                    super_scope,
+                                    "");
+                            }
+                        }
+                    }
+                }
+
+                if let Some(_) = statement_scope_span(self.tcx, super_scope) {
+                    db.note("consider using a `let` binding to increase its lifetime");
+                }
+
+                db.emit();
             }
-            err_borrowed_pointer_too_short(..) => {
+            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
                 let descr = self.cmt_to_path_or_string(&err.cmt);
-                struct_span_err!(self.tcx.sess, span, E0598,
-                                 "lifetime of {} is too short to guarantee \
-                                  its contents can be safely reborrowed",
-                                 descr)
+                let mut db = struct_span_err!(self.tcx.sess, error_span, E0598,
+                                              "lifetime of {} is too short to guarantee \
+                                               its contents can be safely reborrowed",
+                                              descr);
+
+                let descr = match opt_loan_path(&err.cmt) {
+                    Some(lp) => {
+                        format!("`{}`", self.loan_path_to_string(&lp))
+                    }
+                    None => self.cmt_to_string(&err.cmt),
+                };
+                self.tcx.note_and_explain_region(
+                    &mut db,
+                    &format!("{} would have to be valid for ",
+                            descr),
+                    loan_scope,
+                    "...");
+                self.tcx.note_and_explain_region(
+                    &mut db,
+                    &format!("...but {} is only valid for ", descr),
+                    ptr_scope,
+                    "");
+
+                db.emit();
             }
         }
     }
@@ -1035,133 +1236,6 @@ fn region_end_span(&self, region: ty::Region<'tcx>) -> Option<Span> {
         }
     }
 
-    fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
-        let error_span = err.span.clone();
-        match err.code {
-            err_mutbl => {
-                self.note_and_explain_mutbl_error(db, &err, &error_span);
-                self.note_immutability_blame(db, err.cmt.immutability_blame());
-            }
-            err_out_of_scope(super_scope, sub_scope, cause) => {
-                let (value_kind, value_msg) = match err.cmt.cat {
-                    mc::Categorization::Rvalue(..) =>
-                        ("temporary value", "temporary value created here"),
-                    _ =>
-                        ("borrowed value", "borrow occurs here")
-                };
-
-                let is_closure = match cause {
-                    euv::ClosureCapture(s) => {
-                        // The primary span starts out as the closure creation point.
-                        // Change the primary span here to highlight the use of the variable
-                        // in the closure, because it seems more natural. Highlight
-                        // closure creation point as a secondary span.
-                        match db.span.primary_span() {
-                            Some(primary) => {
-                                db.span = MultiSpan::from_span(s);
-                                db.span_label(primary, "capture occurs here");
-                                db.span_label(s, "does not live long enough");
-                                true
-                            }
-                            None => false
-                        }
-                    }
-                    _ => {
-                        db.span_label(error_span, "does not live long enough");
-                        false
-                    }
-                };
-
-                let sub_span = self.region_end_span(sub_scope);
-                let super_span = self.region_end_span(super_scope);
-
-                match (sub_span, super_span) {
-                    (Some(s1), Some(s2)) if s1 == s2 => {
-                        if !is_closure {
-                            db.span = MultiSpan::from_span(s1);
-                            db.span_label(error_span, value_msg);
-                            let msg = match opt_loan_path(&err.cmt) {
-                                None => value_kind.to_string(),
-                                Some(lp) => {
-                                    format!("`{}`", self.loan_path_to_string(&lp))
-                                }
-                            };
-                            db.span_label(s1,
-                                          format!("{} dropped here while still borrowed", msg));
-                        } else {
-                            db.span_label(s1, format!("{} dropped before borrower", value_kind));
-                        }
-                        db.note("values in a scope are dropped in the opposite order \
-                                they are created");
-                    }
-                    (Some(s1), Some(s2)) if !is_closure => {
-                        db.span = MultiSpan::from_span(s2);
-                        db.span_label(error_span, value_msg);
-                        let msg = match opt_loan_path(&err.cmt) {
-                            None => value_kind.to_string(),
-                            Some(lp) => {
-                                format!("`{}`", self.loan_path_to_string(&lp))
-                            }
-                        };
-                        db.span_label(s2, format!("{} dropped here while still borrowed", msg));
-                        db.span_label(s1, format!("{} needs to live until here", value_kind));
-                    }
-                    _ => {
-                        match sub_span {
-                            Some(s) => {
-                                db.span_label(s, format!("{} needs to live until here",
-                                                          value_kind));
-                            }
-                            None => {
-                                self.tcx.note_and_explain_region(
-                                    db,
-                                    "borrowed value must be valid for ",
-                                    sub_scope,
-                                    "...");
-                            }
-                        }
-                        match super_span {
-                            Some(s) => {
-                                db.span_label(s, format!("{} only lives until here", value_kind));
-                            }
-                            None => {
-                                self.tcx.note_and_explain_region(
-                                    db,
-                                    "...but borrowed value is only valid for ",
-                                    super_scope,
-                                    "");
-                            }
-                        }
-                    }
-                }
-
-                if let Some(_) = statement_scope_span(self.tcx, super_scope) {
-                    db.note("consider using a `let` binding to increase its lifetime");
-                }
-            }
-
-            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
-                let descr = match opt_loan_path(&err.cmt) {
-                    Some(lp) => {
-                        format!("`{}`", self.loan_path_to_string(&lp))
-                    }
-                    None => self.cmt_to_string(&err.cmt),
-                };
-                self.tcx.note_and_explain_region(
-                    db,
-                    &format!("{} would have to be valid for ",
-                            descr),
-                    loan_scope,
-                    "...");
-                self.tcx.note_and_explain_region(
-                    db,
-                    &format!("...but {} is only valid for ", descr),
-                    ptr_scope,
-                    "");
-            }
-        }
-    }
-
     fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder, err: &BckError<'tcx>,
                                     error_span: &Span) {
         match err.cmt.note {
index fea9d0d6f1326cafd2ca6df1c44ad14abf24ae47..517b4e7f99b4d06d82ca06ebe14c703e3ac88bb4 100644 (file)
@@ -1201,6 +1201,92 @@ struct Foo<'a> {
 ```
 "##,
 
+E0626: r##"
+This error occurs because a borrow in a generator persists across a
+yield point.
+
+```compile_fail,E0626
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+    let a = &3; // <-- This borrow...
+    yield (); // ...is still in scope here, when the yield occurs.
+    println!("{}", a);
+};
+b.resume();
+```
+
+At present, it is not permitted to have a yield that occurs while a
+borrow is still in scope. To resolve this error, the borrow must
+either be "contained" to a smaller scope that does not overlap the
+yield or else eliminated in another way. So, for example, we might
+resolve the previous example by removing the borrow and just storing
+the integer by value:
+
+```
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+    let a = 3;
+    yield ();
+    println!("{}", a);
+};
+b.resume();
+```
+
+This is a very simple case, of course. In more complex cases, we may
+wish to have more than one reference to the value that was borrowed --
+in those cases, something like the `Rc` or `Arc` types may be useful.
+
+This error also frequently arises with iteration:
+
+```compile_fail,E0626
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+  let v = vec![1,2,3];
+  for &x in &v { // <-- borrow of `v` is still in scope...
+    yield x; // ...when this yield occurs.
+  }
+};
+b.resume();
+```
+
+Such cases can sometimes be resolved by iterating "by value" (or using
+`into_iter()`) to avoid borrowing:
+
+```
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+  let v = vec![1,2,3];
+  for x in v { // <-- Take ownership of the values instead!
+    yield x; // <-- Now yield is OK.
+  }
+};
+b.resume();
+```
+
+If taking ownership is not an option, using indices can work too:
+
+```
+# #![feature(generators, generator_trait)]
+# use std::ops::Generator;
+let mut b = || {
+  let v = vec![1,2,3];
+  let len = v.len(); // (*)
+  for i in 0..len {
+    let x = v[i]; // (*)
+    yield x; // <-- Now yield is OK.
+  }
+};
+b.resume();
+
+// (*) -- Unfortunately, these temporaries are currently required.
+// See <https://github.com/rust-lang/rust/issues/43122>.
+```
+"##,
+
 }
 
 register_diagnostics! {
index 572ce98d3ae8e30a0f19a8b9a69b887c9873eb3e..cc56289a5633467998551c93a0a27facc6ee7b99 100644 (file)
@@ -9,9 +9,11 @@
 // except according to those terms.
 
 use std::fmt;
+use std::iter;
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::{Deref, DerefMut, Range};
+use std::slice;
 use bitslice::{BitSlice, Word};
 use bitslice::{bitwise, Union, Subtract};
 use indexed_vec::Idx;
@@ -21,6 +23,7 @@
 ///
 /// In other words, `T` is the type used to index into the bitvector
 /// this type uses to represent the set of object it holds.
+#[derive(Eq, PartialEq)]
 pub struct IdxSetBuf<T: Idx> {
     _pd: PhantomData<fn(&T)>,
     bits: Vec<Word>,
@@ -109,6 +112,13 @@ pub fn to_owned(&self) -> IdxSetBuf<T> {
         }
     }
 
+    /// Removes all elements
+    pub fn clear(&mut self) {
+        for b in &mut self.bits {
+            *b = 0;
+        }
+    }
+
     /// Removes `elem` from the set `self`; returns true iff this changed `self`.
     pub fn remove(&mut self, elem: &T) -> bool {
         self.bits.clear_bit(elem.index())
@@ -153,4 +163,41 @@ pub fn union(&mut self, other: &IdxSet<T>) -> bool {
     pub fn subtract(&mut self, other: &IdxSet<T>) -> bool {
         bitwise(self.words_mut(), other.words(), &Subtract)
     }
+
+    pub fn iter(&self) -> Iter<T> {
+        Iter {
+            cur: None,
+            iter: self.words().iter().enumerate(),
+            _pd: PhantomData,
+        }
+    }
+}
+
+pub struct Iter<'a, T: Idx> {
+    cur: Option<(Word, usize)>,
+    iter: iter::Enumerate<slice::Iter<'a, Word>>,
+    _pd: PhantomData<fn(&T)>,
+}
+
+impl<'a, T: Idx> Iterator for Iter<'a, T> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<T> {
+        let word_bits = mem::size_of::<Word>() * 8;
+        loop {
+            if let Some((ref mut word, offset)) = self.cur {
+                let bit_pos = word.trailing_zeros() as usize;
+                if bit_pos != word_bits {
+                    let bit = 1 << bit_pos;
+                    *word ^= bit;
+                    return Some(T::new(bit_pos + offset))
+                }
+            }
+
+            match self.iter.next() {
+                Some((i, word)) => self.cur = Some((*word, word_bits * i)),
+                None => return None,
+            }
+        }
+    }
 }
index 1f44378c9e6466f590f1c06aec59f9ef39d3e9a0..42f56aa078276c101d66a15a7fd44435a2485a70 100644 (file)
@@ -38,7 +38,7 @@ impl Idx for u32 {
     fn index(self) -> usize { self as usize }
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct IndexVec<I: Idx, T> {
     pub raw: Vec<T>,
     _marker: PhantomData<Fn(&I)>
index 5e291ea3c152b7e00caa0fcb571f9cd09ef9ba17..866261b4f63dd4600d0a7a3f0a87e8ce80a6d79a 100644 (file)
@@ -292,6 +292,15 @@ fn hash_stable<W: StableHasherResult>(&self,
     }
 }
 
+impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Box<T> {
+    #[inline]
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          ctx: &mut CTX,
+                                          hasher: &mut StableHasher<W>) {
+        (**self).hash_stable(ctx, hasher);
+    }
+}
+
 impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ::std::rc::Rc<T> {
     #[inline]
     fn hash_stable<W: StableHasherResult>(&self,
index 1c03b9f69f711bc24e67f947e9fef0b09c39ff3e..7efa2846cd6f3dd6deb8af749f3bbb46eab26506 100644 (file)
@@ -1006,6 +1006,8 @@ macro_rules! try_with_f {
     passes.push_pass(MIR_OPTIMIZED, mir::transform::deaggregator::Deaggregator);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::copy_prop::CopyPropagation);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyLocals);
+
+    passes.push_pass(MIR_OPTIMIZED, mir::transform::generator::StateTransform);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::CriticalCallEdges);
     passes.push_pass(MIR_OPTIMIZED, mir::transform::dump_mir::Marker("PreTrans"));
 
index aca98df9cc998b98958d0a75a0b854c9af5b8234..31aa8c0316aac78e4f8e7aaf5b93f4456c9f4374 100644 (file)
@@ -607,6 +607,7 @@ fn check_type_for_ffi(&self,
             ty::TyInfer(..) |
             ty::TyError |
             ty::TyClosure(..) |
+            ty::TyGenerator(..) |
             ty::TyProjection(..) |
             ty::TyAnon(..) |
             ty::TyFnDef(..) => bug!("Unexpected type in foreign function"),
index d2ab9b2fbced359c497376a5f550c7cce1de0389..225e271e467b3689b49b3ca63f54d9d827b4c703 100644 (file)
@@ -105,6 +105,7 @@ pub fn provide<$lt>(providers: &mut Providers<$lt>) {
 
         mir
     }
+    generator_sig => { cdata.generator_sig(def_id.index, tcx) }
     mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
     typeck_tables_of => { cdata.item_body_tables(def_id.index, tcx) }
     closure_kind => { cdata.closure_kind(def_id.index) }
index ad3a9dd9fefaf4c354000aef492aa2110e290d7a..2ee5b07347fb31b538113f6889479c6da9dca2f1 100644 (file)
@@ -438,6 +438,7 @@ fn to_def(&self, did: DefId) -> Option<Def> {
             EntryKind::Impl(_) |
             EntryKind::DefaultImpl(_) |
             EntryKind::Field |
+            EntryKind::Generator(_) |
             EntryKind::Closure(_) => return None,
         })
     }
@@ -1100,6 +1101,23 @@ pub fn fn_sig(&self,
         sig.decode((self, tcx))
     }
 
+    fn get_generator_data(&self,
+                      id: DefIndex,
+                      tcx: TyCtxt<'a, 'tcx, 'tcx>)
+                      -> Option<GeneratorData<'tcx>> {
+        match self.entry(id).kind {
+            EntryKind::Generator(data) => Some(data.decode((self, tcx))),
+            _ => None,
+        }
+    }
+
+    pub fn generator_sig(&self,
+                      id: DefIndex,
+                      tcx: TyCtxt<'a, 'tcx, 'tcx>)
+                      -> Option<ty::PolyGenSig<'tcx>> {
+        self.get_generator_data(id, tcx).map(|d| d.sig)
+    }
+
     #[inline]
     pub fn def_key(&self, index: DefIndex) -> DefKey {
         self.def_path_table.def_key(index)
index 8a753a0ae4b573e1a32b53b893dd90dbe262f257..7f14b2bc1056723fb867035d9ad7016b42138a9e 100644 (file)
@@ -1213,13 +1213,23 @@ fn encode_info_for_closure(&mut self, def_id: DefId) -> Entry<'tcx> {
         debug!("IsolatedEncoder::encode_info_for_closure({:?})", def_id);
         let tcx = self.tcx;
 
-        let data = ClosureData {
-            kind: tcx.closure_kind(def_id),
-            sig: self.lazy(&tcx.fn_sig(def_id)),
+        let kind = if let Some(sig) = self.tcx.generator_sig(def_id) {
+            let layout = self.tcx.generator_layout(def_id);
+            let data = GeneratorData {
+                sig,
+                layout: layout.clone(),
+            };
+            EntryKind::Generator(self.lazy(&data))
+        } else {
+            let data = ClosureData {
+                kind: tcx.closure_kind(def_id),
+                sig: self.lazy(&tcx.fn_sig(def_id)),
+            };
+            EntryKind::Closure(self.lazy(&data))
         };
 
         Entry {
-            kind: EntryKind::Closure(self.lazy(&data)),
+            kind,
             visibility: self.lazy(&ty::Visibility::Public),
             span: self.lazy(&tcx.def_span(def_id)),
             attributes: self.encode_attributes(&tcx.get_attrs(def_id)),
index 0b670121ba23b38f2a7318d7286ff9db8943762f..567da85b61c3a59d1b21df9463649655c0f3c404 100644 (file)
@@ -353,6 +353,7 @@ pub enum EntryKind<'tcx> {
     Mod(Lazy<ModData>),
     MacroDef(Lazy<MacroDef>),
     Closure(Lazy<ClosureData<'tcx>>),
+    Generator(Lazy<GeneratorData<'tcx>>),
     Trait(Lazy<TraitData<'tcx>>),
     Impl(Lazy<ImplData<'tcx>>),
     DefaultImpl(Lazy<ImplData<'tcx>>),
@@ -401,6 +402,9 @@ fn hash_stable<W: StableHasherResult>(&self,
             EntryKind::MacroDef(ref macro_def) => {
                 macro_def.hash_stable(hcx, hasher);
             }
+            EntryKind::Generator(data) => {
+                data.hash_stable(hcx, hasher);
+            }
             EntryKind::Closure(closure_data) => {
                 closure_data.hash_stable(hcx, hasher);
             }
@@ -564,3 +568,10 @@ pub struct ClosureData<'tcx> {
     pub sig: Lazy<ty::PolyFnSig<'tcx>>,
 }
 impl_stable_hash_for!(struct ClosureData<'tcx> { kind, sig });
+
+#[derive(RustcEncodable, RustcDecodable)]
+pub struct GeneratorData<'tcx> {
+    pub sig: ty::PolyGenSig<'tcx>,
+    pub layout: mir::GeneratorLayout<'tcx>,
+}
+impl_stable_hash_for!(struct GeneratorData<'tcx> { sig, layout });
index 04c23215463dd9360cc0574c43d30f39a7c4e0f3..72eeaca8b101f3fe92ac5cea2c3d9bf5b1f3bfe8 100644 (file)
@@ -118,6 +118,7 @@ fn expr_as_lvalue(&mut self,
             ExprKind::Return { .. } |
             ExprKind::Literal { .. } |
             ExprKind::InlineAsm { .. } |
+            ExprKind::Yield { .. } |
             ExprKind::Call { .. } => {
                 // these are not lvalues, so we need to make a temporary.
                 debug_assert!(match Category::of(&expr.kind) {
index 8974c4ec1fbfd126907f5aaaccb5232845e7a606..c870e200645400d83d089d65a6b4ed2b2461e1ed 100644 (file)
@@ -179,12 +179,25 @@ fn expr_as_rvalue(&mut self,
 
                 block.and(Rvalue::Aggregate(box AggregateKind::Tuple, fields))
             }
-            ExprKind::Closure { closure_id, substs, upvars } => { // see (*) above
-                let upvars =
+            ExprKind::Closure { closure_id, substs, upvars, interior } => { // see (*) above
+                let mut operands: Vec<_> =
                     upvars.into_iter()
                           .map(|upvar| unpack!(block = this.as_operand(block, scope, upvar)))
                           .collect();
-                block.and(Rvalue::Aggregate(box AggregateKind::Closure(closure_id, substs), upvars))
+                let result = if let Some(interior) = interior {
+                    // Add the state operand
+                    operands.push(Operand::Constant(box Constant {
+                        span: expr_span,
+                        ty: this.hir.tcx().types.u32,
+                        literal: Literal::Value {
+                            value: ConstVal::Integral(ConstInt::U32(0)),
+                        },
+                    }));
+                    box AggregateKind::Generator(closure_id, substs, interior)
+                } else {
+                    box AggregateKind::Closure(closure_id, substs)
+                };
+                block.and(Rvalue::Aggregate(result, operands))
             }
             ExprKind::Adt {
                 adt_def, variant_index, substs, fields, base
@@ -226,6 +239,17 @@ fn expr_as_rvalue(&mut self,
                 block = unpack!(this.stmt_expr(block, expr));
                 block.and(this.unit_rvalue())
             }
+            ExprKind::Yield { value } => {
+                let value = unpack!(block = this.as_operand(block, scope, value));
+                let resume = this.cfg.start_new_block();
+                let cleanup = this.generator_drop_cleanup();
+                this.cfg.terminate(block, source_info, TerminatorKind::Yield {
+                    value: value,
+                    resume: resume,
+                    drop: cleanup,
+                });
+                resume.and(this.unit_rvalue())
+            }
             ExprKind::Literal { .. } |
             ExprKind::Block { .. } |
             ExprKind::Match { .. } |
index 35173bb598c7cfc0384a81a487a4c57ab3f13173..f05411aacab19801c427e9b2cd8d992240394054 100644 (file)
@@ -77,6 +77,7 @@ pub fn of<'tcx>(ek: &ExprKind<'tcx>) -> Option<Category> {
             ExprKind::Borrow { .. } |
             ExprKind::Assign { .. } |
             ExprKind::AssignOp { .. } |
+            ExprKind::Yield { .. } |
             ExprKind::InlineAsm { .. } =>
                 Some(Category::Rvalue(RvalueFunc::AsRvalue)),
 
index 7ae5d6b0ec19aa483151274f9641115f9ecdc12a..96df4037d05e8b93d0bc247e0699c6bb21986b4c 100644 (file)
@@ -284,6 +284,7 @@ pub fn into_expr(&mut self,
             ExprKind::Index { .. } |
             ExprKind::Deref { .. } |
             ExprKind::Literal { .. } |
+            ExprKind::Yield { .. } |
             ExprKind::Field { .. } => {
                 debug_assert!(match Category::of(&expr.kind).unwrap() {
                     Category::Rvalue(RvalueFunc::Into) => false,
index c0b54ce2a84decf7947e90f58321115103895471..e6360bd9570fbbb60010f6db3d084fff32219946 100644 (file)
@@ -712,6 +712,7 @@ fn declare_binding(&mut self,
             ty: var_ty.clone(),
             name: Some(name),
             source_info: source_info,
+            internal: false,
             is_user_variable: true,
         });
         self.var_indices.insert(var_id, var);
index 3353fb9a5b49c8b501ae925658ab12a25852bbc1..96d66ac9986f7d957f687cf154e37409761e1867 100644 (file)
@@ -71,7 +71,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
             // Assume that everything other than closures
             // is a constant "initializer" expression.
             match expr.node {
-                hir::ExprClosure(_, _, body, _) => body,
+                hir::ExprClosure(_, _, body, _, _) => body,
                 _ => hir::BodyId { node_id: expr.id }
             }
         }
@@ -94,13 +94,18 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
 
             let ty = tcx.type_of(tcx.hir.local_def_id(id));
             let mut abi = fn_sig.abi;
-            let implicit_argument = if let ty::TyClosure(..) = ty.sty {
-                // HACK(eddyb) Avoid having RustCall on closures,
-                // as it adds unnecessary (and wrong) auto-tupling.
-                abi = Abi::Rust;
-                Some((closure_self_ty(tcx, id, body_id), None))
-            } else {
-                None
+            let implicit_argument = match ty.sty {
+                ty::TyClosure(..) => {
+                    // HACK(eddyb) Avoid having RustCall on closures,
+                    // as it adds unnecessary (and wrong) auto-tupling.
+                    abi = Abi::Rust;
+                    Some((closure_self_ty(tcx, id, body_id), None))
+                }
+                ty::TyGenerator(..) => {
+                    let gen_ty =  tcx.body_tables(body_id).node_id_to_type(id);
+                    Some((gen_ty, None))
+                }
+                _ => None,
             };
 
             let body = tcx.hir.body(body_id);
@@ -113,7 +118,15 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
                     });
 
             let arguments = implicit_argument.into_iter().chain(explicit_arguments);
-            build::construct_fn(cx, id, arguments, abi, fn_sig.output(), body)
+
+            let (yield_ty, return_ty) = if body.is_generator {
+                let gen_sig = cx.tables().generator_sigs[&id].clone().unwrap();
+                (Some(gen_sig.yield_ty), gen_sig.return_ty)
+            } else {
+                (None, fn_sig.output())
+            };
+
+            build::construct_fn(cx, id, arguments, abi, return_ty, yield_ty, body)
         } else {
             build::construct_const(cx, body_id)
         };
@@ -198,7 +211,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 ///////////////////////////////////////////////////////////////////////////
 // BuildMir -- walks a crate, looking for fn items and methods to build MIR from
 
-fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              closure_expr_id: ast::NodeId,
                              body_id: hir::BodyId)
                              -> Ty<'tcx> {
@@ -326,6 +339,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
                                    arguments: A,
                                    abi: Abi,
                                    return_ty: Ty<'gcx>,
+                                   yield_ty: Option<Ty<'gcx>>,
                                    body: &'gcx hir::Body)
                                    -> Mir<'tcx>
     where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
@@ -334,7 +348,10 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
 
     let tcx = hir.tcx();
     let span = tcx.hir.span(fn_id);
-    let mut builder = Builder::new(hir.clone(), span, arguments.len(), return_ty);
+    let mut builder = Builder::new(hir.clone(),
+        span,
+        arguments.len(),
+        return_ty);
 
     let call_site_extent = CodeExtent::CallSiteScope(body.id());
     let arg_extent = CodeExtent::ParameterScope(body.id());
@@ -387,7 +404,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
         }).collect()
     });
 
-    let mut mir = builder.finish(upvar_decls, return_ty);
+    let mut mir = builder.finish(upvar_decls, return_ty, yield_ty);
     mir.spread_arg = spread_arg;
     mir
 }
@@ -412,7 +429,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
     // Constants can't `return` so a return block should not be created.
     assert_eq!(builder.cached_return_block, None);
 
-    builder.finish(vec![], ty)
+    builder.finish(vec![], ty, None)
 }
 
 fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
@@ -423,7 +440,7 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
     let mut builder = Builder::new(hir, span, 0, ty);
     let source_info = builder.source_info(span);
     builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
-    builder.finish(vec![], ty)
+    builder.finish(vec![], ty, None)
 }
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@@ -458,7 +475,8 @@ fn new(hir: Cx<'a, 'gcx, 'tcx>,
 
     fn finish(self,
               upvar_decls: Vec<UpvarDecl>,
-              return_ty: Ty<'tcx>)
+              return_ty: Ty<'tcx>,
+              yield_ty: Option<Ty<'tcx>>)
               -> Mir<'tcx> {
         for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
             if block.terminator.is_none() {
@@ -470,6 +488,7 @@ fn finish(self,
                  self.visibility_scopes,
                  IndexVec::new(),
                  return_ty,
+                 yield_ty,
                  self.local_decls,
                  self.arg_count,
                  upvar_decls,
@@ -502,6 +521,7 @@ fn args_and_body(&mut self,
                     span: pattern.map_or(self.fn_span, |pat| pat.span)
                 },
                 name: name,
+                internal: false,
                 is_user_variable: false,
             });
         }
index a4a89a7f8185a18946f4b70c9a34916bb07ae820..c35f709a528a2f468c09d3ab85a333381df66d20 100644 (file)
@@ -126,6 +126,9 @@ pub struct Scope<'tcx> {
 
     /// The cache for drop chain on “normal” exit into a particular BasicBlock.
     cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
+
+    /// The cache for drop chain on "generator drop" exit.
+    cached_generator_drop: Option<BasicBlock>,
 }
 
 #[derive(Debug)]
@@ -140,14 +143,22 @@ struct DropData<'tcx> {
     kind: DropKind
 }
 
+#[derive(Debug, Default, Clone, Copy)]
+struct CachedBlock {
+    /// The cached block for the cleanups-on-diverge path. This block
+    /// contains code to run the current drop and all the preceding
+    /// drops (i.e. those having lower index in Drop’s Scope drop
+    /// array)
+    unwind: Option<BasicBlock>,
+
+    /// The cached block for unwinds during cleanups-on-generator-drop path
+    generator_drop: Option<BasicBlock>,
+}
+
 #[derive(Debug)]
 enum DropKind {
     Value {
-        /// The cached block for the cleanups-on-diverge path. This block
-        /// contains code to run the current drop and all the preceding
-        /// drops (i.e. those having lower index in Drop’s Scope drop
-        /// array)
-        cached_block: Option<BasicBlock>
+        cached_block: CachedBlock,
     },
     Storage
 }
@@ -166,6 +177,29 @@ pub struct BreakableScope<'tcx> {
     pub break_destination: Lvalue<'tcx>,
 }
 
+impl CachedBlock {
+    fn invalidate(&mut self) {
+        self.generator_drop = None;
+        self.unwind = None;
+    }
+
+    fn get(&self, generator_drop: bool) -> Option<BasicBlock> {
+        if generator_drop {
+            self.generator_drop
+        } else {
+            self.unwind
+        }
+    }
+
+    fn ref_mut(&mut self, generator_drop: bool) -> &mut Option<BasicBlock> {
+        if generator_drop {
+            &mut self.generator_drop
+        } else {
+            &mut self.unwind
+        }
+    }
+}
+
 impl DropKind {
     fn may_panic(&self) -> bool {
         match *self {
@@ -187,7 +221,7 @@ fn invalidate_cache(&mut self, unwind: bool) {
         if !unwind { return; }
         for dropdata in &mut self.drops {
             if let DropKind::Value { ref mut cached_block } = dropdata.kind {
-                *cached_block = None;
+                cached_block.invalidate();
             }
         }
     }
@@ -196,10 +230,12 @@ fn invalidate_cache(&mut self, unwind: bool) {
     ///
     /// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for
     /// this method to work correctly.
-    fn cached_block(&self) -> Option<BasicBlock> {
+    fn cached_block(&self, generator_drop: bool) -> Option<BasicBlock> {
         let mut drops = self.drops.iter().rev().filter_map(|data| {
             match data.kind {
-                DropKind::Value { cached_block } => Some(cached_block),
+                DropKind::Value { cached_block } => {
+                    Some(cached_block.get(generator_drop))
+                }
                 DropKind::Storage => None
             }
         });
@@ -294,6 +330,7 @@ pub fn push_scope(&mut self, extent: (CodeExtent, SourceInfo)) {
             extent_span: extent.1.span,
             needs_cleanup: false,
             drops: vec![],
+            cached_generator_drop: None,
             cached_exits: FxHashMap()
         });
     }
@@ -319,7 +356,8 @@ pub fn pop_scope(&mut self,
                                           &scope,
                                           &self.scopes,
                                           block,
-                                          self.arg_count));
+                                          self.arg_count,
+                                          false));
 
         self.cfg.push_end_region(block, extent.1, scope.extent);
         block.unit()
@@ -370,7 +408,8 @@ pub fn exit_scope(&mut self,
                                               scope,
                                               rest,
                                               block,
-                                              self.arg_count));
+                                              self.arg_count,
+                                              false));
 
             // End all regions for scopes out of which we are breaking.
             self.cfg.push_end_region(block, extent.1, scope.extent);
@@ -381,6 +420,63 @@ pub fn exit_scope(&mut self,
                            TerminatorKind::Goto { target: target });
     }
 
+    /// Creates a path that performs all required cleanup for dropping a generator.
+    ///
+    /// This path terminates in GeneratorDrop. Returns the start of the path.
+    /// None indicates there’s no cleanup to do at this point.
+    pub fn generator_drop_cleanup(&mut self) -> Option<BasicBlock> {
+        if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
+            return None;
+        }
+
+        // Fill in the cache
+        self.diverge_cleanup_gen(true);
+
+        let src_info = self.scopes[0].source_info(self.fn_span);
+        let tmp = self.get_unit_temp();
+        let mut block = self.cfg.start_new_block();
+        let result = block;
+        let mut rest = &mut self.scopes[..];
+
+        while let Some((scope, rest_)) = {rest}.split_last_mut() {
+            rest = rest_;
+            if !scope.needs_cleanup {
+                continue;
+            }
+            block = if let Some(b) = scope.cached_generator_drop {
+                self.cfg.terminate(block, src_info,
+                                   TerminatorKind::Goto { target: b });
+                return Some(result);
+            } else {
+                let b = self.cfg.start_new_block();
+                scope.cached_generator_drop = Some(b);
+                self.cfg.terminate(block, src_info,
+                                   TerminatorKind::Goto { target: b });
+                b
+            };
+            unpack!(block = build_scope_drops(&mut self.cfg,
+                                              scope,
+                                              rest,
+                                              block,
+                                              self.arg_count,
+                                              true));
+
+            // End all regions for scopes out of which we are breaking.
+            self.cfg.push_end_region(block, src_info, scope.extent);
+
+            if let Some(ref free_data) = scope.free {
+                let next = self.cfg.start_new_block();
+                let free = build_free(self.hir.tcx(), &tmp, free_data, next);
+                self.cfg.terminate(block, scope.source_info(free_data.span), free);
+                block = next;
+            }
+        }
+
+        self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop);
+
+        Some(result)
+    }
+
     /// Creates a new visibility scope, nested in the current one.
     pub fn new_visibility_scope(&mut self, span: Span) -> VisibilityScope {
         let parent = self.visibility_scope;
@@ -465,7 +561,8 @@ pub fn local_scope(&self) -> Option<CodeExtent> {
                 None,
             MirSource::Fn(_) =>
                 Some(self.topmost_scope()),
-            MirSource::Promoted(..) =>
+            MirSource::Promoted(..) |
+            MirSource::GeneratorDrop(..) =>
                 bug!(),
         }
     }
@@ -481,7 +578,7 @@ pub fn schedule_drop(&mut self,
                          lvalue_ty: Ty<'tcx>) {
         let needs_drop = self.hir.needs_drop(lvalue_ty);
         let drop_kind = if needs_drop {
-            DropKind::Value { cached_block: None }
+            DropKind::Value { cached_block: CachedBlock::default() }
         } else {
             // Only temps and vars need their storage dead.
             match *lvalue {
@@ -567,6 +664,10 @@ pub fn schedule_drop(&mut self,
     /// See module comment for more details. None indicates there’s no
     /// cleanup to do at this point.
     pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
+        self.diverge_cleanup_gen(false)
+    }
+
+    fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> Option<BasicBlock> {
         if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
             return None;
         }
@@ -600,7 +701,7 @@ pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
 
         for scope in scopes.iter_mut() {
             target = build_diverge_scope(
-                hir.tcx(), cfg, scope.extent_span, scope, target);
+                hir.tcx(), cfg, scope.extent_span, scope, target, generator_drop);
         }
         Some(target)
     }
@@ -677,7 +778,8 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
                            scope: &Scope<'tcx>,
                            earlier_scopes: &[Scope<'tcx>],
                            mut block: BasicBlock,
-                           arg_count: usize)
+                           arg_count: usize,
+                           generator_drop: bool)
                            -> BlockAnd<()> {
     debug!("build_scope_drops({:?} -> {:?})", block, scope);
     let mut iter = scope.drops.iter().rev().peekable();
@@ -689,16 +791,20 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
                 // for us to diverge into in case the drop panics.
                 let on_diverge = iter.peek().iter().filter_map(|dd| {
                     match dd.kind {
-                        DropKind::Value { cached_block: None } =>
-                            span_bug!(drop_data.span, "cached block not present?"),
-                        DropKind::Value { cached_block } => cached_block,
+                        DropKind::Value { cached_block } => {
+                            let result = cached_block.get(generator_drop);
+                            if result.is_none() {
+                                span_bug!(drop_data.span, "cached block not present?")
+                            }
+                            result
+                        },
                         DropKind::Storage => None
                     }
                 }).next();
                 // If there’s no `cached_block`s within current scope,
                 // we must look for one in the enclosing scope.
                 let on_diverge = on_diverge.or_else(|| {
-                    earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
+                    earlier_scopes.iter().rev().flat_map(|s| s.cached_block(generator_drop)).next()
                 });
                 let next = cfg.start_new_block();
                 cfg.terminate(block, source_info, TerminatorKind::Drop {
@@ -711,6 +817,11 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
             DropKind::Storage => {}
         }
 
+        // We do not need to emit StorageDead for generator drops
+        if generator_drop {
+            continue
+        }
+
         // Drop the storage for both value and storage drops.
         // Only temps and vars need their storage dead.
         match drop_data.location {
@@ -730,7 +841,8 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(_tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                        cfg: &mut CFG<'tcx>,
                                        span: Span,
                                        scope: &mut Scope<'tcx>,
-                                       mut target: BasicBlock)
+                                       mut target: BasicBlock,
+                                       generator_drop: bool)
                                        -> BasicBlock
 {
     // Build up the drops in **reverse** order. The end result will
@@ -744,7 +856,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(_tcx: TyCtxt<'a, 'gcx, 'tcx>,
     // The code in this function reads from right to left. At each
     // point, we check for cached blocks representing the
     // remainder. If everything is cached, we'll just walk right to
-    // left reading the cached results but never created anything.
+    // left reading the cached results but never create anything.
 
     let visibility_scope = scope.visibility_scope;
     let source_info = |span| SourceInfo {
@@ -766,7 +878,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(_tcx: TyCtxt<'a, 'gcx, 'tcx>,
         // match the behavior of clang, but on inspection eddyb says
         // this is not what clang does.
         let cached_block = match drop_data.kind {
-            DropKind::Value { ref mut cached_block } => cached_block,
+            DropKind::Value { ref mut cached_block } => cached_block.ref_mut(generator_drop),
             DropKind::Storage => continue
         };
         target = if let Some(cached_block) = *cached_block {
index d7ad9f9c09aef820ea7b5a0dcb48c1353bda1ac4..6392bde9eaeb447401f909b43fb68a8eecd7cf47 100644 (file)
@@ -452,15 +452,21 @@ fn propagate_bits_into_graph_successors_of(
         match bb_data.terminator().kind {
             mir::TerminatorKind::Return |
             mir::TerminatorKind::Resume |
+            mir::TerminatorKind::GeneratorDrop |
             mir::TerminatorKind::Unreachable => {}
             mir::TerminatorKind::Goto { ref target } |
             mir::TerminatorKind::Assert { ref target, cleanup: None, .. } |
+            mir::TerminatorKind::Yield { resume: ref target, drop: None, .. } |
             mir::TerminatorKind::Drop { ref target, location: _, unwind: None } |
             mir::TerminatorKind::DropAndReplace {
                 ref target, value: _, location: _, unwind: None
             } => {
                 self.propagate_bits_into_entry_set_for(in_out, changed, target);
             }
+            mir::TerminatorKind::Yield { resume: ref target, drop: Some(ref drop), .. } => {
+                self.propagate_bits_into_entry_set_for(in_out, changed, target);
+                self.propagate_bits_into_entry_set_for(in_out, changed, drop);
+            }
             mir::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
             mir::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
             mir::TerminatorKind::DropAndReplace {
index 20a61418d85a57731d12d5c947aada26945e2ff3..01ce77bce13c11dec77b5af490a1dc4ac31784d9 100644 (file)
@@ -470,6 +470,7 @@ fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
         match term.kind {
             TerminatorKind::Goto { target: _ } |
             TerminatorKind::Resume |
+            TerminatorKind::GeneratorDrop |
             TerminatorKind::Unreachable => { }
 
             TerminatorKind::Return => {
@@ -481,6 +482,10 @@ fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) {
                 // branching terminators - these don't move anything
             }
 
+            TerminatorKind::Yield { ref value,  .. } => {
+                self.gather_operand(loc, value);
+            }
+
             TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
                 self.gather_move(loc, location);
             }
index b7fc37675e29c64c42bdaff82e3a708b7c60d37b..231e2fb68e65ca175234389f56f42a59cf9c1348 100644 (file)
@@ -430,8 +430,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
         hir::ExprClosure(..) => {
             let closure_ty = cx.tables().expr_ty(expr);
-            let (def_id, substs) = match closure_ty.sty {
-                ty::TyClosure(def_id, substs) => (def_id, substs),
+            let (def_id, substs, interior) = match closure_ty.sty {
+                ty::TyClosure(def_id, substs) => (def_id, substs, None),
+                ty::TyGenerator(def_id, substs, interior) => (def_id, substs, Some(interior)),
                 _ => {
                     span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
                 }
@@ -446,6 +447,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 closure_id: def_id,
                 substs: substs,
                 upvars: upvars,
+                interior,
             }
         }
 
@@ -563,6 +565,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
         }
         hir::ExprArray(ref fields) => ExprKind::Array { fields: fields.to_ref() },
         hir::ExprTup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() },
+
+        hir::ExprYield(ref v) => ExprKind::Yield { value: v.to_ref() },
     };
 
     Expr {
@@ -697,56 +701,65 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             });
             let region = cx.tcx.mk_region(region);
 
-            let self_expr = match cx.tcx.closure_kind(closure_def_id) {
-                ty::ClosureKind::Fn => {
-                    let ref_closure_ty = cx.tcx.mk_ref(region,
-                                                       ty::TypeAndMut {
-                                                           ty: closure_ty,
-                                                           mutbl: hir::MutImmutable,
-                                                       });
-                    Expr {
-                        ty: closure_ty,
-                        temp_lifetime: temp_lifetime,
-                        span: expr.span,
-                        kind: ExprKind::Deref {
-                            arg: Expr {
-                                ty: ref_closure_ty,
-                                temp_lifetime: temp_lifetime,
-                                span: expr.span,
-                                kind: ExprKind::SelfRef,
-                            }
-                            .to_ref(),
-                        },
+            let self_expr = if let ty::TyClosure(..) = closure_ty.sty {
+                match cx.tcx.closure_kind(closure_def_id) {
+                    ty::ClosureKind::Fn => {
+                        let ref_closure_ty = cx.tcx.mk_ref(region,
+                                                           ty::TypeAndMut {
+                                                               ty: closure_ty,
+                                                               mutbl: hir::MutImmutable,
+                                                           });
+                        Expr {
+                            ty: closure_ty,
+                            temp_lifetime: temp_lifetime,
+                            span: expr.span,
+                            kind: ExprKind::Deref {
+                                arg: Expr {
+                                    ty: ref_closure_ty,
+                                    temp_lifetime: temp_lifetime,
+                                    span: expr.span,
+                                    kind: ExprKind::SelfRef,
+                                }
+                                .to_ref(),
+                            },
+                        }
                     }
-                }
-                ty::ClosureKind::FnMut => {
-                    let ref_closure_ty = cx.tcx.mk_ref(region,
-                                                       ty::TypeAndMut {
-                                                           ty: closure_ty,
-                                                           mutbl: hir::MutMutable,
-                                                       });
-                    Expr {
-                        ty: closure_ty,
-                        temp_lifetime: temp_lifetime,
-                        span: expr.span,
-                        kind: ExprKind::Deref {
-                            arg: Expr {
-                                ty: ref_closure_ty,
-                                temp_lifetime: temp_lifetime,
-                                span: expr.span,
-                                kind: ExprKind::SelfRef,
-                            }.to_ref(),
-                        },
+                    ty::ClosureKind::FnMut => {
+                        let ref_closure_ty = cx.tcx.mk_ref(region,
+                                                           ty::TypeAndMut {
+                                                               ty: closure_ty,
+                                                               mutbl: hir::MutMutable,
+                                                           });
+                        Expr {
+                            ty: closure_ty,
+                            temp_lifetime: temp_lifetime,
+                            span: expr.span,
+                            kind: ExprKind::Deref {
+                                arg: Expr {
+                                    ty: ref_closure_ty,
+                                    temp_lifetime: temp_lifetime,
+                                    span: expr.span,
+                                    kind: ExprKind::SelfRef,
+                                }.to_ref(),
+                            },
+                        }
                     }
-                }
-                ty::ClosureKind::FnOnce => {
-                    Expr {
-                        ty: closure_ty,
-                        temp_lifetime: temp_lifetime,
-                        span: expr.span,
-                        kind: ExprKind::SelfRef,
+                    ty::ClosureKind::FnOnce => {
+                        Expr {
+                            ty: closure_ty,
+                            temp_lifetime: temp_lifetime,
+                            span: expr.span,
+                            kind: ExprKind::SelfRef,
+                        }
                     }
                 }
+            } else {
+                Expr {
+                    ty: closure_ty,
+                    temp_lifetime: temp_lifetime,
+                    span: expr.span,
+                    kind: ExprKind::SelfRef,
+                }
             };
 
             // at this point we have `self.n`, which loads up the upvar
index 2f4ab36d394b00a488e623b47ef7cc8fd5ff1a70..46df40e42871db1f5f6f47623e6612832d71922c 100644 (file)
@@ -61,6 +61,7 @@ pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, src: MirSource) -> Cx<'a, 'gcx,
         let constness = match src {
             MirSource::Const(_) |
             MirSource::Static(..) => hir::Constness::Const,
+            MirSource::GeneratorDrop(..) => hir::Constness::NotConst,
             MirSource::Fn(id) => {
                 let fn_like = FnLikeNode::from_node(infcx.tcx.hir.get(id));
                 fn_like.map_or(hir::Constness::NotConst, |f| f.constness())
index e46c18b2dcfe44a1a72c698e8036c28b4d8f93db..073b32ae7fa321a7b7d3963bf52084fe810664aa 100644 (file)
@@ -19,7 +19,7 @@
 use rustc::hir::def_id::DefId;
 use rustc::middle::region::CodeExtent;
 use rustc::ty::subst::Substs;
-use rustc::ty::{self, AdtDef, ClosureSubsts, Region, Ty};
+use rustc::ty::{self, AdtDef, ClosureSubsts, Region, Ty, GeneratorInterior};
 use rustc::hir;
 use syntax::ast;
 use syntax_pos::Span;
@@ -238,6 +238,7 @@ pub enum ExprKind<'tcx> {
         closure_id: DefId,
         substs: ClosureSubsts<'tcx>,
         upvars: Vec<ExprRef<'tcx>>,
+        interior: Option<GeneratorInterior<'tcx>>,
     },
     Literal {
         literal: Literal<'tcx>,
@@ -247,6 +248,9 @@ pub enum ExprKind<'tcx> {
         outputs: Vec<ExprRef<'tcx>>,
         inputs: Vec<ExprRef<'tcx>>
     },
+    Yield {
+        value: ExprRef<'tcx>,
+    },
 }
 
 #[derive(Clone, Debug)]
index 62e762be93a71938a7a3f93ccfdc40d1186a43f7..177211e9372595343d92b4155246b05909b22b18 100644 (file)
@@ -128,6 +128,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
     LocalDecl {
         mutability, ty, name: None,
         source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, span },
+        internal: false,
         is_user_variable: false
     }
 }
@@ -148,6 +149,12 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
 {
     debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
 
+    // Check if this is a generator, if so, return the drop glue for it
+    if let Some(&ty::TyS { sty: ty::TyGenerator(gen_def_id, substs, _), .. }) = ty {
+        let mir = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap();
+        return mir.subst(tcx, substs.substs);
+    }
+
     let substs = if let Some(ty) = ty {
         tcx.mk_substs(iter::once(Kind::from(ty)))
     } else {
@@ -178,6 +185,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
         ),
         IndexVec::new(),
         sig.output(),
+        None,
         local_decls_for_sig(&sig, span),
         sig.inputs().len(),
         vec![],
@@ -213,10 +221,10 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
 }
 
 pub struct DropShimElaborator<'a, 'tcx: 'a> {
-    mir: &'a Mir<'tcx>,
-    patch: MirPatch<'tcx>,
-    tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
+    pub mir: &'a Mir<'tcx>,
+    pub patch: MirPatch<'tcx>,
+    pub tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
+    pub param_env: ty::ParamEnv<'tcx>,
 }
 
 impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> {
@@ -390,6 +398,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
         ),
         IndexVec::new(),
         sig.output(),
+        None,
         local_decls,
         sig.inputs().len(),
         vec![],
@@ -461,6 +470,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
         ),
         IndexVec::new(),
         sig.output(),
+        None,
         local_decls,
         sig.inputs().len(),
         vec![],
index dec0717e9e38355454b4f12311cd79cfdfd92ece..d3bbd4d78ed385cccd083c9311f7eab430c712ef 100644 (file)
@@ -60,6 +60,7 @@ fn run_pass<'a, 'tcx>(&self,
                     return
                 }
             }
+            MirSource::GeneratorDrop(_) => (),
         }
 
         // We only run when the MIR optimization level is > 1.
index b158cb43ce7a922c4192bcad6ea79ae43367c733..308a48677e58f97b86d7a404810c443606532d42 100644 (file)
@@ -96,15 +96,18 @@ fn find_dead_unwinds<'a, 'tcx>(
                            MaybeInitializedLvals::new(tcx, mir, &env),
                            |bd, p| &bd.move_data().move_paths[p]);
     for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
-        match bb_data.terminator().kind {
+        let location = match bb_data.terminator().kind {
             TerminatorKind::Drop { ref location, unwind: Some(_), .. } |
-            TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => {
+            TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => location,
+            _ => continue,
+        };
+
                 let mut init_data = InitializationData {
                     live: flow_inits.sets().on_entry_set_for(bb.index()).to_owned(),
                     dead: IdxSetBuf::new_empty(env.move_data.move_paths.len()),
                 };
                 debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
-                       bb, bb_data, init_data.live);
+                        bb, bb_data, init_data.live);
                 for stmt in 0..bb_data.statements.len() {
                     let loc = Location { block: bb, statement_index: stmt };
                     init_data.apply_location(tcx, mir, env, loc);
@@ -130,9 +133,6 @@ fn find_dead_unwinds<'a, 'tcx>(
                 if !maybe_live {
                     dead_unwinds.add(&bb);
                 }
-            }
-            _ => {}
-        }
     }
 
     dead_unwinds
@@ -314,7 +314,7 @@ fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) {
         let patch = &mut self.patch;
         debug!("create_drop_flag({:?})", self.mir.span);
         self.drop_flags.entry(index).or_insert_with(|| {
-            patch.new_temp(tcx.types.bool, span)
+            patch.new_internal(tcx.types.bool, span)
         });
     }
 
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
new file mode 100644 (file)
index 0000000..29c24ef
--- /dev/null
@@ -0,0 +1,727 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Transforms generators into state machines
+
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::middle::const_val::ConstVal;
+use rustc::mir::*;
+use rustc::mir::transform::{MirPass, MirSource};
+use rustc::mir::visit::{LvalueContext, MutVisitor};
+use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
+use rustc::ty::subst::{Kind, Substs};
+use util::dump_mir;
+use util::liveness;
+use rustc_const_math::ConstInt;
+use rustc_data_structures::indexed_vec::Idx;
+use std::collections::HashMap;
+use std::borrow::Cow;
+use std::iter::once;
+use std::mem;
+use transform::simplify;
+use transform::no_landing_pads::no_landing_pads;
+
+pub struct StateTransform;
+
+struct RenameLocalVisitor {
+    from: Local,
+    to: Local,
+}
+
+impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor {
+    fn visit_local(&mut self,
+                        local: &mut Local) {
+        if *local == self.from {
+            *local = self.to;
+        }
+    }
+}
+
+struct DerefArgVisitor;
+
+impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor {
+    fn visit_lvalue(&mut self,
+                    lvalue: &mut Lvalue<'tcx>,
+                    context: LvalueContext<'tcx>,
+                    location: Location) {
+        if *lvalue == Lvalue::Local(Local::new(1)) {
+            *lvalue = Lvalue::Projection(Box::new(Projection {
+                base: lvalue.clone(),
+                elem: ProjectionElem::Deref,
+            }));
+        } else {
+            self.super_lvalue(lvalue, context, location);
+        }
+    }
+}
+
+struct TransformVisitor<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    state_adt_ref: &'tcx AdtDef,
+    state_substs: &'tcx Substs<'tcx>,
+
+    // The index of the generator state in the generator struct
+    state_field: usize,
+
+    // Mapping from Local to (type of local, generator struct index)
+    remap: HashMap<Local, (Ty<'tcx>, usize)>,
+
+    // The number of generator states. 0 is unresumed, 1 is poisoned. So this is initialized to 2
+    bb_target_count: u32,
+
+    // Map from a (which block to resume execution at, which block to use to drop the generator)
+    // to a generator state
+    bb_targets: HashMap<(BasicBlock, Option<BasicBlock>), u32>,
+
+    // The original RETURN_POINTER local
+    new_ret_local: Local,
+
+    // The block to resume execution when for Return
+    return_block: BasicBlock,
+}
+
+impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
+    // Make a GeneratorState rvalue
+    fn make_state(&self, idx: usize, val: Operand<'tcx>) -> Rvalue<'tcx> {
+        let adt = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None);
+        Rvalue::Aggregate(box adt, vec![val])
+    }
+
+    // Create a Lvalue referencing a generator struct field
+    fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Lvalue<'tcx> {
+        let base = Lvalue::Local(Local::new(1));
+        let field = Projection {
+            base: base,
+            elem: ProjectionElem::Field(Field::new(idx), ty),
+        };
+        Lvalue::Projection(Box::new(field))
+    }
+
+    // Create a statement which changes the generator state
+    fn set_state(&self, state_disc: u32, source_info: SourceInfo) -> Statement<'tcx> {
+        let state = self.make_field(self.state_field, self.tcx.types.u32);
+        let val = Operand::Constant(box Constant {
+            span: source_info.span,
+            ty: self.tcx.types.u32,
+            literal: Literal::Value {
+                value: ConstVal::Integral(ConstInt::U32(state_disc)),
+            },
+        });
+        Statement {
+            source_info,
+            kind: StatementKind::Assign(state, Rvalue::Use(val)),
+        }
+    }
+}
+
+impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
+    fn visit_lvalue(&mut self,
+                    lvalue: &mut Lvalue<'tcx>,
+                    context: LvalueContext<'tcx>,
+                    location: Location) {
+        if let Lvalue::Local(l) = *lvalue {
+            // Replace an Local in the remap with a generator struct access
+            if let Some(&(ty, idx)) = self.remap.get(&l) {
+                *lvalue = self.make_field(idx, ty);
+            }
+        } else {
+            self.super_lvalue(lvalue, context, location);
+        }
+    }
+
+    fn visit_basic_block_data(&mut self,
+                              block: BasicBlock,
+                              data: &mut BasicBlockData<'tcx>) {
+        let ret_val = match data.terminator().kind {
+            TerminatorKind::Return => Some((1,
+                self.return_block,
+                Operand::Consume(Lvalue::Local(self.new_ret_local)),
+                None)),
+            TerminatorKind::Yield { ref value, resume, drop } => Some((0,
+                resume,
+                value.clone(),
+                drop)),
+            _ => None
+        };
+
+        // Remove StorageLive and StorageDead statements for remapped locals
+        data.retain_statements(|s| {
+            match s.kind {
+                StatementKind::StorageLive(ref l) | StatementKind::StorageDead(ref l) => {
+                    if let Lvalue::Local(l) = *l {
+                        !self.remap.contains_key(&l)
+                    } else {
+                        true
+                    }
+                }
+                _ => true
+            }
+        });
+
+        if let Some((state_idx, resume, v, drop)) = ret_val {
+            let bb_idx = {
+                let bb_targets = &mut self.bb_targets;
+                let bb_target = &mut self.bb_target_count;
+                *bb_targets.entry((resume, drop)).or_insert_with(|| {
+                    let target = *bb_target;
+                    *bb_target = target.checked_add(1).unwrap();
+                    target
+                })
+            };
+            let source_info = data.terminator().source_info;
+            data.statements.push(self.set_state(bb_idx, source_info));
+            data.statements.push(Statement {
+                source_info,
+                kind: StatementKind::Assign(Lvalue::Local(RETURN_POINTER),
+                    self.make_state(state_idx, v)),
+            });
+            data.terminator.as_mut().unwrap().kind = TerminatorKind::Return;
+        }
+
+        self.super_basic_block_data(block, data);
+    }
+}
+
+fn make_generator_state_argument_indirect<'a, 'tcx>(
+                tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                def_id: DefId,
+                mir: &mut Mir<'tcx>) {
+    let gen_ty = mir.local_decls.raw[1].ty;
+
+    let region = ty::ReFree(ty::FreeRegion {
+        scope: def_id,
+        bound_region: ty::BoundRegion::BrEnv,
+    });
+
+    let region = tcx.mk_region(region);
+
+    let ref_gen_ty = tcx.mk_ref(region, ty::TypeAndMut {
+        ty: gen_ty,
+        mutbl: hir::MutMutable
+    });
+
+    // Replace the by value generator argument
+    mir.local_decls.raw[1].ty = ref_gen_ty;
+
+    // Add a deref to accesses of the generator state
+    DerefArgVisitor.visit_mir(mir);
+}
+
+fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
+                            mir: &mut Mir<'tcx>) -> Local {
+    let source_info = SourceInfo {
+        span: mir.span,
+        scope: ARGUMENT_VISIBILITY_SCOPE,
+    };
+
+    let new_ret = LocalDecl {
+        mutability: Mutability::Mut,
+        ty: ret_ty,
+        name: None,
+        source_info,
+        internal: false,
+        is_user_variable: false,
+    };
+    let new_ret_local = Local::new(mir.local_decls.len());
+    mir.local_decls.push(new_ret);
+    mir.local_decls.swap(0, new_ret_local.index());
+
+    RenameLocalVisitor {
+        from: RETURN_POINTER,
+        to: new_ret_local,
+    }.visit_mir(mir);
+
+    new_ret_local
+}
+
+fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                               mir: &Mir<'tcx>,
+                                               source: MirSource) -> liveness::LocalSet {
+    let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
+    let result = liveness::liveness_of_locals(mir);
+    liveness::dump_mir(tcx, "generator_liveness", source, mir, &result);
+
+    for (block, data) in mir.basic_blocks().iter_enumerated() {
+        if let TerminatorKind::Yield { .. } = data.terminator().kind {
+            set.union(&result.outs[block]);
+        }
+    }
+
+    // The generator argument is ignored
+    set.remove(&Local::new(1));
+
+    set
+}
+
+fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                            source: MirSource,
+                            interior: GeneratorInterior<'tcx>,
+                            mir: &mut Mir<'tcx>)
+    -> (HashMap<Local, (Ty<'tcx>, usize)>, GeneratorLayout<'tcx>)
+{
+    // Use a liveness analysis to compute locals which are live across a suspension point
+    let live_locals = locals_live_across_suspend_points(tcx, mir, source);
+
+    // Erase regions from the types passed in from typeck so we can compare them with
+    // MIR types
+    let allowed = tcx.erase_regions(&interior.as_slice());
+
+    for (local, decl) in mir.local_decls.iter_enumerated() {
+        // Ignore locals which are internal or not live
+        if !live_locals.contains(&local) || decl.internal {
+            continue;
+        }
+
+        // Sanity check that typeck knows about the type of locals which are
+        // live across a suspension point
+        if !allowed.contains(&decl.ty) {
+            span_bug!(mir.span,
+                      "Broken MIR: generator contains type {} in MIR, \
+                       but typeck only knows about {}",
+                      decl.ty,
+                      interior);
+        }
+    }
+
+    let upvar_len = mir.upvar_decls.len();
+    let dummy_local = LocalDecl::new_internal(tcx.mk_nil(), mir.span);
+
+    // Gather live locals and their indices replacing values in mir.local_decls with a dummy
+    // to avoid changing local indices
+    let live_decls = live_locals.iter().map(|local| {
+        let var = mem::replace(&mut mir.local_decls[local], dummy_local.clone());
+        (local, var)
+    });
+
+    // Create a map from local indices to generator struct indices.
+    // These are offset by (upvar_len + 1) because of fields which comes before locals.
+    // We also create a vector of the LocalDecls of these locals.
+    let (remap, vars) = live_decls.enumerate().map(|(idx, (local, var))| {
+        ((local, (var.ty, upvar_len + 1 + idx)), var)
+    }).unzip();
+
+    let layout = GeneratorLayout {
+        fields: vars
+    };
+
+    (remap, layout)
+}
+
+fn insert_entry_point<'tcx>(mir: &mut Mir<'tcx>,
+                            block: BasicBlockData<'tcx>) {
+    mir.basic_blocks_mut().raw.insert(0, block);
+
+    let blocks = mir.basic_blocks_mut().iter_mut();
+
+    for target in blocks.flat_map(|b| b.terminator_mut().successors_mut()) {
+        *target = BasicBlock::new(target.index() + 1);
+    }
+}
+
+fn elaborate_generator_drops<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                      def_id: DefId,
+                                      mir: &mut Mir<'tcx>) {
+    use util::elaborate_drops::{elaborate_drop, Unwind};
+    use util::patch::MirPatch;
+    use shim::DropShimElaborator;
+
+    let param_env = tcx.param_env(def_id);
+    let gen = Local::new(1);
+
+    for block in mir.basic_blocks().indices() {
+        let (target, unwind, source_info) = match mir.basic_blocks()[block].terminator() {
+            &Terminator {
+                source_info,
+                kind: TerminatorKind::Drop {
+                    location: Lvalue::Local(local),
+                    target,
+                    unwind
+                }
+            } if local == gen => (target, unwind, source_info),
+            _ => continue,
+        };
+        let unwind = if let Some(unwind) = unwind {
+            Unwind::To(unwind)
+        } else {
+            Unwind::InCleanup
+        };
+        let patch = {
+            let mut elaborator = DropShimElaborator {
+                mir: &mir,
+                patch: MirPatch::new(mir),
+                tcx,
+                param_env
+            };
+            elaborate_drop(
+                &mut elaborator,
+                source_info,
+                &Lvalue::Local(gen),
+                (),
+                target,
+                unwind,
+                block
+            );
+            elaborator.patch
+        };
+        patch.apply(mir);
+    }
+}
+
+fn create_generator_drop_shim<'a, 'tcx>(
+                tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                transform: &TransformVisitor<'a, 'tcx>,
+                def_id: DefId,
+                source: MirSource,
+                gen_ty: Ty<'tcx>,
+                mir: &Mir<'tcx>,
+                drop_clean: BasicBlock) -> Mir<'tcx> {
+    let mut mir = mir.clone();
+
+    let source_info = SourceInfo {
+        span: mir.span,
+        scope: ARGUMENT_VISIBILITY_SCOPE,
+    };
+
+    let return_block = BasicBlock::new(mir.basic_blocks().len());
+    mir.basic_blocks_mut().push(BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: TerminatorKind::Return,
+        }),
+        is_cleanup: false,
+    });
+
+    let mut cases: Vec<_> = transform.bb_targets.iter().filter_map(|(&(_, u), &s)| {
+        u.map(|d| (s, d))
+    }).collect();
+
+    cases.insert(0, (0, drop_clean));
+
+    // The poisoned state 1 falls through to the default case which is just to return
+
+    let switch = TerminatorKind::SwitchInt {
+        discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)),
+        switch_ty: tcx.types.u32,
+        values: Cow::from(cases.iter().map(|&(i, _)| {
+                ConstInt::U32(i)
+            }).collect::<Vec<_>>()),
+        targets: cases.iter().map(|&(_, d)| d).chain(once(return_block)).collect(),
+    };
+
+    insert_entry_point(&mut mir, BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: switch,
+        }),
+        is_cleanup: false,
+    });
+
+    for block in mir.basic_blocks_mut() {
+        let kind = &mut block.terminator_mut().kind;
+        if let TerminatorKind::GeneratorDrop = *kind {
+            *kind = TerminatorKind::Return;
+        }
+    }
+
+    // Replace the return variable
+    let source_info = SourceInfo {
+        span: mir.span,
+        scope: ARGUMENT_VISIBILITY_SCOPE,
+    };
+
+    mir.return_ty = tcx.mk_nil();
+    mir.local_decls[RETURN_POINTER] = LocalDecl {
+        mutability: Mutability::Mut,
+        ty: tcx.mk_nil(),
+        name: None,
+        source_info,
+        internal: false,
+        is_user_variable: false,
+    };
+
+    make_generator_state_argument_indirect(tcx, def_id, &mut mir);
+
+    // Change the generator argument from &mut to *mut
+    mir.local_decls[Local::new(1)] = LocalDecl {
+        mutability: Mutability::Mut,
+        ty: tcx.mk_ptr(ty::TypeAndMut {
+            ty: gen_ty,
+            mutbl: hir::Mutability::MutMutable,
+        }),
+        name: None,
+        source_info,
+        internal: false,
+        is_user_variable: false,
+    };
+
+    no_landing_pads(tcx, &mut mir);
+
+    // Make sure we remove dead blocks to remove
+    // unrelated code from the resume part of the function
+    simplify::remove_dead_blocks(&mut mir);
+
+    dump_mir(tcx, None, "generator_drop", &0, source, &mut mir);
+
+    mir
+}
+
+fn insert_panic_on_resume_after_return<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                        mir: &mut Mir<'tcx>) {
+    let assert_block = BasicBlock::new(mir.basic_blocks().len());
+    let term = TerminatorKind::Assert {
+        cond: Operand::Constant(box Constant {
+            span: mir.span,
+            ty: tcx.types.bool,
+            literal: Literal::Value {
+                value: ConstVal::Bool(false),
+            },
+        }),
+        expected: true,
+        msg: AssertMessage::GeneratorResumedAfterReturn,
+        target: assert_block,
+        cleanup: None,
+    };
+
+    let source_info = SourceInfo {
+        span: mir.span,
+        scope: ARGUMENT_VISIBILITY_SCOPE,
+    };
+
+    mir.basic_blocks_mut().push(BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: term,
+        }),
+        is_cleanup: false,
+    });
+}
+
+fn creator_generator_resume_function<'a, 'tcx>(
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        mut transform: TransformVisitor<'a, 'tcx>,
+        def_id: DefId,
+        source: MirSource,
+        mir: &mut Mir<'tcx>) {
+    // Poison the generator when it unwinds
+    for block in mir.basic_blocks_mut() {
+        let source_info = block.terminator().source_info;
+        if let &TerminatorKind::Resume = &block.terminator().kind {
+            block.statements.push(transform.set_state(1, source_info));
+        }
+    }
+
+    let source_info = SourceInfo {
+        span: mir.span,
+        scope: ARGUMENT_VISIBILITY_SCOPE,
+    };
+
+    let poisoned_block = BasicBlock::new(mir.basic_blocks().len());
+
+    let term = TerminatorKind::Assert {
+        cond: Operand::Constant(box Constant {
+            span: mir.span,
+            ty: tcx.types.bool,
+            literal: Literal::Value {
+                value: ConstVal::Bool(false),
+            },
+        }),
+        expected: true,
+        msg: AssertMessage::GeneratorResumedAfterPanic,
+        target: transform.return_block,
+        cleanup: None,
+    };
+
+    mir.basic_blocks_mut().push(BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: term,
+        }),
+        is_cleanup: false,
+    });
+
+    transform.bb_targets.insert((poisoned_block, None), 1);
+
+    let switch = TerminatorKind::SwitchInt {
+        discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)),
+        switch_ty: tcx.types.u32,
+        values: Cow::from(transform.bb_targets.values().map(|&i| {
+                ConstInt::U32(i)
+            }).collect::<Vec<_>>()),
+        targets: transform.bb_targets.keys()
+            .map(|&(k, _)| k)
+            .chain(once(transform.return_block))
+            .collect(),
+    };
+
+    insert_entry_point(mir, BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: switch,
+        }),
+        is_cleanup: false,
+    });
+
+    make_generator_state_argument_indirect(tcx, def_id, mir);
+
+    no_landing_pads(tcx, &mut mir);
+
+    // Make sure we remove dead blocks to remove
+    // unrelated code from the drop part of the function
+    simplify::remove_dead_blocks(mir);
+
+    dump_mir(tcx, None, "generator_resume", &0, source, mir);
+}
+
+fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock {
+    let source_info = SourceInfo {
+        span: mir.span,
+        scope: ARGUMENT_VISIBILITY_SCOPE,
+    };
+
+    let return_block = BasicBlock::new(mir.basic_blocks().len());
+    mir.basic_blocks_mut().push(BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: TerminatorKind::Return,
+        }),
+        is_cleanup: false,
+    });
+
+    // Create a block to destroy an unresumed generators. This can only destroy upvars.
+    let drop_clean = BasicBlock::new(mir.basic_blocks().len());
+    let term = TerminatorKind::Drop {
+        location: Lvalue::Local(Local::new(1)),
+        target: return_block,
+        unwind: None,
+    };
+    mir.basic_blocks_mut().push(BasicBlockData {
+        statements: Vec::new(),
+        terminator: Some(Terminator {
+            source_info,
+            kind: term,
+        }),
+        is_cleanup: false,
+    });
+
+    drop_clean
+}
+
+impl MirPass for StateTransform {
+    fn run_pass<'a, 'tcx>(&self,
+                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    source: MirSource,
+                    mir: &mut Mir<'tcx>) {
+        let yield_ty = if let Some(yield_ty) = mir.yield_ty {
+            yield_ty
+        } else {
+            // This only applies to generators
+            return
+        };
+
+        assert!(mir.generator_drop.is_none());
+
+        let node_id = source.item_id();
+        let def_id = tcx.hir.local_def_id(source.item_id());
+
+        // Get the interior types which typeck computed
+        let interior = *tcx.typeck_tables_of(def_id).generator_interiors.get(&node_id).unwrap();
+
+        // The first argument is the generator type passed by value
+        let gen_ty = mir.local_decls.raw[1].ty;
+
+        // Compute GeneratorState<yield_ty, return_ty>
+        let state_did = tcx.lang_items.gen_state().unwrap();
+        let state_adt_ref = tcx.adt_def(state_did);
+        let state_substs = tcx.mk_substs([Kind::from(yield_ty),
+            Kind::from(mir.return_ty)].iter());
+        let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+
+        // We rename RETURN_POINTER which has type mir.return_ty to new_ret_local
+        // RETURN_POINTER then is a fresh unused local with type ret_ty.
+        let new_ret_local = replace_result_variable(ret_ty, mir);
+
+        // Extract locals which are live across suspension point into `layout`
+        // `remap` gives a mapping from local indices onto generator struct indices
+        let (remap, layout) = compute_layout(tcx, source, interior, mir);
+
+        let state_field = mir.upvar_decls.len();
+
+        let mut bb_targets = HashMap::new();
+
+        // If we jump to the entry point, we should go to the initial 0 generator state.
+        // FIXME: Could this result in the need for destruction for state 0?
+        bb_targets.insert((BasicBlock::new(0), None), 0);
+
+        // Run the transformation which converts Lvalues from Local to generator struct
+        // accesses for locals in `remap`.
+        // It also rewrites `return x` and `yield y` as writing a new generator state and returning
+        // GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively.
+        let mut transform = TransformVisitor {
+            tcx,
+            state_adt_ref,
+            state_substs,
+            remap,
+            bb_target_count: 2,
+            bb_targets,
+            new_ret_local,
+            state_field,
+
+            // For returns we will resume execution at the next added basic block.
+            // This happens in `insert_panic_on_resume_after_return`
+            return_block: BasicBlock::new(mir.basic_blocks().len()),
+        };
+        transform.visit_mir(mir);
+
+        // Update our MIR struct to reflect the changed we've made
+        mir.return_ty = ret_ty;
+        mir.yield_ty = None;
+        mir.arg_count = 1;
+        mir.spread_arg = None;
+        mir.generator_layout = Some(layout);
+
+        // Panic if we resumed after returning
+        insert_panic_on_resume_after_return(tcx, mir);
+
+        // Insert `drop(generator_struct)` which is used to drop upvars for generators in
+        // the unresumed (0) state.
+        // This is expanded to a drop ladder in `elaborate_generator_drops`.
+        let drop_clean = insert_clean_drop(mir);
+
+        dump_mir(tcx, None, "generator_pre-elab", &0, source, mir);
+
+        // Expand `drop(generator_struct)` to a drop ladder which destroys upvars.
+        // If any upvars are moved out of, drop elaboration will handle upvar destruction.
+        // However we need to also elaborate the code generated by `insert_clean_drop`.
+        elaborate_generator_drops(tcx, def_id, mir);
+
+        dump_mir(tcx, None, "generator_post-transform", &0, source, mir);
+
+        // Create a copy of our MIR and use it to create the drop shim for the generator
+        let drop_shim = create_generator_drop_shim(tcx,
+            &transform,
+            def_id,
+            source,
+            gen_ty,
+            &mir,
+            drop_clean);
+
+        mir.generator_drop = Some(box drop_shim);
+
+        // Create the Generator::resume function
+        creator_generator_resume_function(tcx, transform, def_id, source, mir);
+    }
+}
index d3fee8045e6e3bd916c9c2831e461cb8e1eb8f63..e7ee68b104b41b34f59766ec3be6c1d2e75945e3 100644 (file)
@@ -175,6 +175,10 @@ fn should_inline(&self,
             return false;
         }
 
+        // Cannot inline generators which haven't been transformed yet
+        if callee_mir.yield_ty.is_some() {
+            return false;
+        }
 
         let attrs = tcx.get_attrs(callsite.callee);
         let hint = attr::find_inline_attr(None, &attrs[..]);
@@ -652,6 +656,8 @@ fn visit_terminator_kind(&mut self, block: BasicBlock,
         self.super_terminator_kind(block, kind, loc);
 
         match *kind {
+            TerminatorKind::GeneratorDrop |
+            TerminatorKind::Yield { .. } => bug!(),
             TerminatorKind::Goto { ref mut target} => {
                 *target = self.update_target(*target);
             }
index a247ce2231e769195ec4b13a10b1a60a8086d4f0..555bea63c27dd4faccfbf2bcb07e3747558bbd86 100644 (file)
@@ -40,6 +40,7 @@
 pub mod deaggregator;
 pub mod instcombine;
 pub mod copy_prop;
+pub mod generator;
 pub mod inline;
 pub mod nll;
 
index 8595663ba18c4ea4051feaffa15dcd975f039ab3..fa6bb644871dced4e11184f0bfa68d6f9eac738c 100644 (file)
@@ -43,6 +43,8 @@ fn visit_terminator(&mut self,
             TerminatorKind::Resume |
             TerminatorKind::Return |
             TerminatorKind::Unreachable |
+            TerminatorKind::GeneratorDrop |
+            TerminatorKind::Yield { .. } |
             TerminatorKind::SwitchInt { .. } => {
                 /* nothing to do */
             },
index e1c4602b045ebab53404a86b715176f6c4a7c84e..f46a9a1a6fa9fe5ccea9f2c3afdd3d60de1671f1 100644 (file)
@@ -392,6 +392,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                 }).into_iter().collect(),
                 IndexVec::new(),
                 ty,
+                None,
                 initial_locals,
                 0,
                 vec![],
index 8321d9b99ae8d63ba3ea7cf05d7375431d119770..53e3a7ff5a2351e42bfb4fcbc9fdd4cdb44a0a2d 100644 (file)
@@ -375,6 +375,8 @@ fn qualify_const(&mut self) -> Qualif {
                 TerminatorKind::SwitchInt {..} |
                 TerminatorKind::DropAndReplace { .. } |
                 TerminatorKind::Resume |
+                TerminatorKind::GeneratorDrop |
+                TerminatorKind::Yield { .. } |
                 TerminatorKind::Unreachable => None,
 
                 TerminatorKind::Return => {
@@ -966,6 +968,7 @@ fn run_pass<'a, 'tcx>(&self,
             }
             MirSource::Static(_, hir::MutImmutable) => Mode::Static,
             MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
+            MirSource::GeneratorDrop(_) |
             MirSource::Const(_) |
             MirSource::Promoted(..) => return
         };
index f3a82b54063b4710c1fd594c0f0aa093b8784505..787f1ae1e21adc0186d7a58b28eeb7270ab13daf 100644 (file)
@@ -34,7 +34,10 @@ fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
 macro_rules! span_mirbug {
     ($context:expr, $elem:expr, $($message:tt)*) => ({
         mirbug($context.tcx(), $context.last_span,
-               &format!("broken MIR ({:?}): {}", $elem, format!($($message)*)))
+               &format!("broken MIR in {:?} ({:?}): {}",
+                        $context.body_id,
+                        $elem,
+                        format_args!($($message)*)))
     })
 }
 
@@ -60,6 +63,7 @@ struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
     cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
     mir: &'a Mir<'tcx>,
     last_span: Span,
+    body_id: ast::NodeId,
     errors_reported: bool
 }
 
@@ -108,6 +112,7 @@ fn visit_mir(&mut self, mir: &Mir<'tcx>) {
 impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
     fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
         TypeVerifier {
+            body_id: cx.body_id,
             cx: cx,
             mir: mir,
             last_span: mir.span,
@@ -297,6 +302,19 @@ fn field_ty(&mut self,
                         })
                     }
                 }
+                ty::TyGenerator(def_id, substs, _) => {
+                    // Try upvars first. `field_tys` requires final optimized MIR.
+                    if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field.index()) {
+                        return Ok(ty);
+                    }
+
+                    return match substs.field_tys(def_id, tcx).nth(field.index()) {
+                        Some(ty) => Ok(ty),
+                        None => Err(FieldAccessError::OutOfRange {
+                            field_count: substs.field_tys(def_id, tcx).count() + 1
+                        })
+                    }
+                }
                 ty::TyTuple(tys, _) => {
                     return match tys.get(field.index()) {
                         Some(&ty) => Ok(ty),
@@ -428,6 +446,7 @@ fn check_terminator(&mut self,
             TerminatorKind::Goto { .. } |
             TerminatorKind::Resume |
             TerminatorKind::Return |
+            TerminatorKind::GeneratorDrop |
             TerminatorKind::Unreachable |
             TerminatorKind::Drop { .. } => {
                 // no checks needed for these
@@ -494,6 +513,22 @@ fn check_terminator(&mut self,
                     }
                 }
             }
+            TerminatorKind::Yield { ref value, .. } => {
+                let value_ty = value.ty(mir, tcx);
+                match mir.yield_ty {
+                    None => span_mirbug!(self, term, "yield in non-generator"),
+                    Some(ty) => {
+                        if let Err(terr) = self.sub_types(value_ty, ty) {
+                            span_mirbug!(self,
+                                term,
+                                "type of yield value is {:?}, but the yield type is {:?}: {:?}",
+                                value_ty,
+                                ty,
+                                terr);
+                        }
+                    }
+                }
+            }
         }
     }
 
@@ -620,6 +655,20 @@ fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>)
                     span_mirbug!(self, block, "return on cleanup block")
                 }
             }
+            TerminatorKind::GeneratorDrop { .. } => {
+                if is_cleanup {
+                    span_mirbug!(self, block, "generator_drop in cleanup block")
+                }
+            }
+            TerminatorKind::Yield { resume, drop, .. } => {
+                if is_cleanup {
+                    span_mirbug!(self, block, "yield in cleanup block")
+                }
+                self.assert_iscleanup(mir, block, resume, is_cleanup);
+                if let Some(drop) = drop {
+                    self.assert_iscleanup(mir, block, drop, is_cleanup);
+                }
+            }
             TerminatorKind::Unreachable => {}
             TerminatorKind::Drop { target, unwind, .. } |
             TerminatorKind::DropAndReplace { target, unwind, .. } |
index da7e218439cf0bfe21bef73e70fe8e81faee7f81..203bda16a5c66c10eed429c0797e59f59a8cf662 100644 (file)
@@ -752,7 +752,8 @@ fn drop_loop_pair(&mut self, ety: Ty<'tcx>, ptr_based: bool) -> BasicBlock {
     fn open_drop<'a>(&mut self) -> BasicBlock {
         let ty = self.lvalue_ty(self.lvalue);
         match ty.sty {
-            ty::TyClosure(def_id, substs) => {
+            ty::TyClosure(def_id, substs) |
+            ty::TyGenerator(def_id, substs, _) => {
                 let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
                 self.open_drop_for_tuple(&tys)
             }
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
new file mode 100644 (file)
index 0000000..fd15c90
--- /dev/null
@@ -0,0 +1,249 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Liveness analysis which computes liveness of MIR local variables at the boundary of basic blocks
+//!
+//! This analysis considers references as being used only at the point of the
+//! borrow. This means that this does not track uses because of references that
+//! already exist:
+//!
+//! ```Rust
+//!     fn foo() {
+//!         x = 0;
+//!         // `x` is live here
+//!         GLOBAL = &x: *const u32;
+//!         // but not here, even while it can be accessed through `GLOBAL`.
+//!         foo();
+//!         x = 1;
+//!         // `x` is live again here, because it is assigned to `OTHER_GLOBAL`
+//!         OTHER_GLOBAL = &x: *const u32;
+//!         // ...
+//!     }
+//! ```
+//!
+//! This means that users of this analysis still have to check whether
+//! pre-existing references can be used to access the value (e.g. at movable
+//! generator yield points, all pre-existing references are invalidated, so this
+//! doesn't matter).
+
+use rustc::mir::*;
+use rustc::mir::visit::{LvalueContext, Visitor};
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_data_structures::indexed_set::IdxSetBuf;
+use util::pretty::{write_basic_block, dump_enabled, write_mir_intro};
+use rustc::mir::transform::MirSource;
+use rustc::ty::item_path;
+use std::path::{PathBuf, Path};
+use std::fs;
+use rustc::ty::TyCtxt;
+use std::io::{self, Write};
+
+pub type LocalSet = IdxSetBuf<Local>;
+
+#[derive(Eq, PartialEq, Clone)]
+struct BlockInfo {
+    defs: LocalSet,
+    uses: LocalSet,
+}
+
+struct BlockInfoVisitor {
+    pre_defs: LocalSet,
+    defs: LocalSet,
+    uses: LocalSet,
+}
+
+impl<'tcx> Visitor<'tcx> for BlockInfoVisitor {
+    fn visit_lvalue(&mut self,
+                    lvalue: &Lvalue<'tcx>,
+                    context: LvalueContext<'tcx>,
+                    location: Location) {
+        if let Lvalue::Local(local) = *lvalue {
+            match context {
+                LvalueContext::Store |
+
+                // We let Call defined the result in both the success and unwind cases.
+                // This may not be right.
+                LvalueContext::Call |
+
+                // Storage live and storage dead aren't proper defines, but we can ignore
+                // values that come before them.
+                LvalueContext::StorageLive |
+                LvalueContext::StorageDead => {
+                    self.defs.add(&local);
+                }
+                LvalueContext::Projection(..) |
+
+                // Borrows only consider their local used at the point of the borrow.
+                // This won't affect the results since we use this analysis for generators
+                // and we only care about the result at suspension points. Borrows cannot
+                // cross suspension points so this behavior is unproblematic.
+                LvalueContext::Borrow { .. } |
+
+                LvalueContext::Inspect |
+                LvalueContext::Consume |
+                LvalueContext::Validate |
+
+                // We consider drops to always be uses of locals.
+                // Drop eloboration should be run before this analysis otherwise
+                // the results might be too pessimistic.
+                LvalueContext::Drop => {
+                    // Ignore uses which are already defined in this block
+                    if !self.pre_defs.contains(&local) {
+                        self.uses.add(&local);
+                    }
+                }
+            }
+        }
+
+        self.super_lvalue(lvalue, context, location)
+    }
+}
+
+fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo {
+    let mut visitor = BlockInfoVisitor {
+        pre_defs: LocalSet::new_empty(locals),
+        defs: LocalSet::new_empty(locals),
+        uses: LocalSet::new_empty(locals),
+    };
+
+    let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 };
+
+    for statement in &b.statements {
+        visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
+        visitor.pre_defs.union(&visitor.defs);
+    }
+    visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location);
+
+    BlockInfo {
+        defs: visitor.defs,
+        uses: visitor.uses,
+    }
+}
+
+// This gives the result of the liveness analysis at the boundary of basic blocks
+pub struct LivenessResult {
+    pub ins: IndexVec<BasicBlock, LocalSet>,
+    pub outs: IndexVec<BasicBlock, LocalSet>,
+}
+
+pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
+    let locals = mir.local_decls.len();
+    let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
+        block(b, locals)
+    }).collect();
+
+    let copy = |from: &IndexVec<BasicBlock, LocalSet>, to: &mut IndexVec<BasicBlock, LocalSet>| {
+        for (i, set) in to.iter_enumerated_mut() {
+            set.clone_from(&from[i]);
+        }
+    };
+
+    let mut ins: IndexVec<_, _> = mir.basic_blocks()
+        .indices()
+        .map(|_| LocalSet::new_empty(locals)).collect();
+    let mut outs = ins.clone();
+
+    let mut ins_ = ins.clone();
+    let mut outs_ = outs.clone();
+
+    loop {
+        copy(&ins, &mut ins_);
+        copy(&outs, &mut outs_);
+
+        for b in mir.basic_blocks().indices().rev() {
+            // out = ∪ {ins of successors}
+            outs[b].clear();
+            for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
+                outs[b].union(&ins[successor]);
+            }
+
+            // in = use ∪ (out - def)
+            ins[b].clone_from(&outs[b]);
+            ins[b].subtract(&def_use[b].defs);
+            ins[b].union(&def_use[b].uses);
+        }
+
+        if ins_ == ins && outs_ == outs {
+            break;
+        }
+    }
+
+    LivenessResult {
+        ins,
+        outs,
+    }
+}
+
+pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          pass_name: &str,
+                          source: MirSource,
+                          mir: &Mir<'tcx>,
+                          result: &LivenessResult) {
+    if !dump_enabled(tcx, pass_name, source) {
+        return;
+    }
+    let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
+        tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
+    });
+    dump_matched_mir_node(tcx, pass_name, &node_path,
+                          source, mir, result);
+}
+
+fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                   pass_name: &str,
+                                   node_path: &str,
+                                   source: MirSource,
+                                   mir: &Mir<'tcx>,
+                                   result: &LivenessResult) {
+    let mut file_path = PathBuf::new();
+    if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
+        let p = Path::new(file_dir);
+        file_path.push(p);
+    };
+    let file_name = format!("rustc.node{}{}-liveness.mir",
+                            source.item_id(), pass_name);
+    file_path.push(&file_name);
+    let _ = fs::File::create(&file_path).and_then(|mut file| {
+        writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
+        writeln!(file, "// source = {:?}", source)?;
+        writeln!(file, "// pass_name = {}", pass_name)?;
+        writeln!(file, "")?;
+        write_mir_fn(tcx, source, mir, &mut file, result)?;
+        Ok(())
+    });
+}
+
+pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                              src: MirSource,
+                              mir: &Mir<'tcx>,
+                              w: &mut Write,
+                              result: &LivenessResult)
+                              -> io::Result<()> {
+    write_mir_intro(tcx, src, mir, w)?;
+    for block in mir.basic_blocks().indices() {
+        let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
+            let live: Vec<String> = mir.local_decls.indices()
+                .filter(|i| result[block].contains(i))
+                .map(|i| format!("{:?}", i))
+                .collect();
+            writeln!(w, "{} {{{}}}", prefix, live.join(", "))
+        };
+        print(w, "   ", &result.ins)?;
+        write_basic_block(tcx, block, mir, w)?;
+        print(w, "   ", &result.outs)?;
+        if block.index() + 1 != mir.basic_blocks().len() {
+            writeln!(w, "")?;
+        }
+    }
+
+    writeln!(w, "}}")?;
+    Ok(())
+}
+
index 4386bab38c0399650bb461a6a6d7cfc16cacedbe..b03fd0196a369dc9fba5711e76139866f2dba91f 100644 (file)
@@ -14,6 +14,7 @@
 
 mod graphviz;
 mod pretty;
+pub mod liveness;
 
 pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty};
 pub use self::graphviz::{write_mir_graphviz};
index ac121131eb999544419365ae272069e03642dd68..3cf7858406787122b421af5750fc67dddf6cdb85 100644 (file)
@@ -101,6 +101,13 @@ pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
         Local::new(index as usize)
     }
 
+    pub fn new_internal(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+        let index = self.next_local;
+        self.next_local += 1;
+        self.new_locals.push(LocalDecl::new_internal(ty, span));
+        Local::new(index as usize)
+    }
+
     pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
         let block = BasicBlock::new(self.patch_map.len());
         debug!("MirPatch: new_block: {:?}: {:?}", block, data);
index 22a8c4378d4c34236ba39a05045c8d5f08f8147e..0811783a9e57f7b75c211652c2398cc84b346c81 100644 (file)
@@ -94,6 +94,7 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                    mir: &Mir<'tcx>) {
     let promotion_id = match source {
         MirSource::Promoted(_, id) => format!("-{:?}", id),
+        MirSource::GeneratorDrop(_) => format!("-drop"),
         _ => String::new()
     };
 
@@ -120,6 +121,9 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         writeln!(file, "// source = {:?}", source)?;
         writeln!(file, "// pass_name = {}", pass_name)?;
         writeln!(file, "// disambiguator = {}", disambiguator)?;
+        if let Some(ref layout) = mir.generator_layout {
+            writeln!(file, "// generator_layout = {:?}", layout)?;
+        }
         writeln!(file, "")?;
         write_mir_fn(tcx, source, mir, &mut file)?;
         Ok(())
@@ -176,7 +180,7 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 }
 
 /// Write out a human-readable textual representation for the given basic block.
-fn write_basic_block(tcx: TyCtxt,
+pub fn write_basic_block(tcx: TyCtxt,
                      block: BasicBlock,
                      mir: &Mir,
                      w: &mut Write)
@@ -274,7 +278,7 @@ fn write_scope_tree(tcx: TyCtxt,
 
 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
 /// local variables (both user-defined bindings and compiler temporaries).
-fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              src: MirSource,
                              mir: &Mir,
                              w: &mut Write)
@@ -322,28 +326,34 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
         MirSource::Const(_) => write!(w, "const")?,
         MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
         MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
-        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
+        MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?,
+        MirSource::GeneratorDrop(_) => write!(w, "drop_glue")?,
     }
 
     item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere
         write!(w, " {}", tcx.node_path_str(src.item_id()))
     })?;
 
-    if let MirSource::Fn(_) = src {
-        write!(w, "(")?;
-
-        // fn argument types.
-        for (i, arg) in mir.args_iter().enumerate() {
-            if i != 0 {
-                write!(w, ", ")?;
+    match src {
+        MirSource::Fn(_) | MirSource::GeneratorDrop(_) => {
+            write!(w, "(")?;
+
+            // fn argument types.
+            for (i, arg) in mir.args_iter().enumerate() {
+                if i != 0 {
+                    write!(w, ", ")?;
+                }
+                write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
             }
-            write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
-        }
 
-        write!(w, ") -> {}", mir.return_ty)
-    } else {
-        assert_eq!(mir.arg_count, 0);
-        write!(w, ": {} =", mir.return_ty)
+            write!(w, ") -> {}", mir.return_ty)
+        }
+        MirSource::Const(..) |
+        MirSource::Static(..) |
+        MirSource::Promoted(..) => {
+            assert_eq!(mir.arg_count, 0);
+            write!(w, ": {} =", mir.return_ty)
+        }
     }
 }
 
index 8443cc8267d1c87dc1fad2fe439cbd846db361ab..d0322e27d66be9f1808fa79a66cfee274354002f 100644 (file)
@@ -435,6 +435,9 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
         hir::ExprAgain(_) |
         hir::ExprRet(_) |
 
+        // Generator expressions
+        hir::ExprYield(_) |
+
         // Expressions with side-effects.
         hir::ExprAssign(..) |
         hir::ExprAssignOp(..) |
index 21a4c007fb1926b35144ee74db78919dd8ebf430..8b6aeabcc12efe3867dabfb5dbbe2d9cc5f1efee 100644 (file)
@@ -81,7 +81,7 @@ fn visit_expr(&mut self, e: &'hir hir::Expr) {
             hir::ExprLoop(ref b, _, source) => {
                 self.with_context(Loop(LoopKind::Loop(source)), |v| v.visit_block(&b));
             }
-            hir::ExprClosure(.., b, _) => {
+            hir::ExprClosure(.., b, _, _) => {
                 self.with_context(Closure, |v| v.visit_nested_body(b));
             }
             hir::ExprBreak(label, ref opt_expr) => {
index d5e477ff0c784d218d958dddff7ca883e12f9bd8..aff792f10cedb4c51df807b9cc228f08fe467f95 100644 (file)
@@ -159,6 +159,8 @@ fn visit_terminator_kind(&mut self,
             TerminatorKind::DropAndReplace { .. } => "TerminatorKind::DropAndReplace",
             TerminatorKind::Call { .. } => "TerminatorKind::Call",
             TerminatorKind::Assert { .. } => "TerminatorKind::Assert",
+            TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
+            TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
         }, kind);
         self.super_terminator_kind(block, kind, location);
     }
@@ -170,6 +172,12 @@ fn visit_assert_message(&mut self,
         self.record(match *msg {
             AssertMessage::BoundsCheck { .. } => "AssertMessage::BoundsCheck",
             AssertMessage::Math(..) => "AssertMessage::Math",
+            AssertMessage::GeneratorResumedAfterReturn => {
+                "AssertMessage::GeneratorResumedAfterReturn"
+            }
+            AssertMessage::GeneratorResumedAfterPanic => {
+                "AssertMessage::GeneratorResumedAfterPanic"
+            }
         }, msg);
         self.super_assert_message(msg, location);
     }
@@ -197,6 +205,7 @@ fn visit_rvalue(&mut self,
                     AggregateKind::Tuple => "AggregateKind::Tuple",
                     AggregateKind::Adt(..) => "AggregateKind::Adt",
                     AggregateKind::Closure(..) => "AggregateKind::Closure",
+                    AggregateKind::Generator(..) => "AggregateKind::Generator",
                 }, kind);
 
                 "Rvalue::Aggregate"
index d1c1dd7436a5ba5fae0ea61492cb6ddf22cc4bb2..11db23732fba3e2dd5f5ed5abd31d66c95f3f089 100644 (file)
@@ -77,6 +77,12 @@ pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
             if variant_index > 0 { bug!("{} is a closure, which only has one variant", t);}
             substs.upvar_tys(def_id, cx.tcx()).collect()
         },
+        ty::TyGenerator(def_id, substs, _) => {
+            if variant_index > 0 { bug!("{} is a generator, which only has one variant", t);}
+            substs.field_tys(def_id, cx.tcx()).map(|t| {
+                cx.tcx().normalize_associated_type(&t)
+            }).collect()
+        },
         _ => bug!("{} is not a type that can have fields.", t)
     }
 }
index b31295f4022ed3580b333b8c0682712e413d537a..559acd53a360299923e921ebf206e9a38f046b03 100644 (file)
@@ -629,6 +629,8 @@ fn visit_terminator_kind(&mut self,
             mir::TerminatorKind::Return |
             mir::TerminatorKind::Unreachable |
             mir::TerminatorKind::Assert { .. } => {}
+            mir::TerminatorKind::GeneratorDrop |
+            mir::TerminatorKind::Yield { .. } => bug!(),
         }
 
         self.super_terminator_kind(block, kind, location);
index 0cc499577bb7caaa5c98ac97ecb029866e214654..184c6f83579302241b0bd6c25c6264f1e1ab4096 100644 (file)
 use value::Value;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::layout::{Layout, LayoutTyper};
-use rustc::ty::subst::{Subst, Substs};
+use rustc::ty::subst::{Kind, Subst, Substs};
 use rustc::hir;
 
 use libc::{c_uint, c_char};
 use std::iter;
 
+use syntax::abi::Abi;
 use syntax::attr;
 use syntax::symbol::InternedString;
 use syntax_pos::Span;
@@ -91,6 +92,16 @@ pub fn type_pair_fields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
                 }
             }))
         }
+        ty::TyGenerator(def_id, substs, _) => {
+            let mut tys = substs.field_tys(def_id, ccx.tcx());
+            tys.next().and_then(|first_ty| tys.next().and_then(|second_ty| {
+                if tys.next().is_some() {
+                    None
+                } else {
+                    Some([first_ty, second_ty])
+                }
+            }))
+        }
         ty::TyTuple(tys, _) => {
             if tys.len() != 2 {
                 return None;
@@ -511,6 +522,28 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
                 sig.abi
             ))
         }
+        ty::TyGenerator(def_id, substs, _) => {
+            let tcx = ccx.tcx();
+            let sig = tcx.generator_sig(def_id).unwrap().subst(tcx, substs.substs);
+
+            let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
+            let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
+
+            sig.map_bound(|sig| {
+                let state_did = tcx.lang_items.gen_state().unwrap();
+                let state_adt_ref = tcx.adt_def(state_did);
+                let state_substs = tcx.mk_substs([Kind::from(sig.yield_ty),
+                    Kind::from(sig.return_ty)].iter());
+                let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+
+                tcx.mk_fn_sig(iter::once(env_ty),
+                    ret_ty,
+                    false,
+                    hir::Unsafety::Normal,
+                    Abi::Rust
+                )
+            })
+        }
         _ => bug!("unexpected type {:?} to ty_fn_sig", ty)
     }
 }
index 61204b88e130ef715d518f0a72e6d077c895d837..399176461dfae2c348d00829a81b98dd3547b9df 100644 (file)
@@ -580,6 +580,16 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                    unique_type_id,
                                    usage_site_span).finalize(cx)
         }
+        ty::TyGenerator(def_id, substs, _) => {
+            let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx()).map(|t| {
+                cx.tcx().normalize_associated_type(&t)
+            }).collect();
+            prepare_tuple_metadata(cx,
+                                   t,
+                                   &upvar_tys,
+                                   unique_type_id,
+                                   usage_site_span).finalize(cx)
+        }
         ty::TyAdt(def, ..) => match def.adt_kind() {
             AdtKind::Struct => {
                 prepare_struct_metadata(cx,
index 6e36073107b561ef60747cf558001443bcf2f860..826b4c09cc2d3714be97c5ac9e840d0b98cff0a1 100644 (file)
@@ -165,6 +165,9 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyClosure(..) => {
             output.push_str("closure");
         }
+        ty::TyGenerator(..) => {
+            output.push_str("generator");
+        }
         ty::TyError |
         ty::TyInfer(_) |
         ty::TyProjection(..) |
index 598af1cda91d4940fdc7e703d1eaa56bd6d64694..a17ddabb1a7cdade098131471633a1e615b865bd 100644 (file)
@@ -216,8 +216,10 @@ fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
                 TerminatorKind::Goto { .. } |
                 TerminatorKind::Resume |
                 TerminatorKind::Return |
+                TerminatorKind::GeneratorDrop |
                 TerminatorKind::Unreachable |
-                TerminatorKind::SwitchInt { .. } => {
+                TerminatorKind::SwitchInt { .. } |
+                TerminatorKind::Yield { .. }  => {
                     /* nothing to do */
                 }
                 TerminatorKind::Call { cleanup: unwind, .. } |
index 9bb29c340d9839de4a3b7454fa0a36d73ef3f37b..edf833a26307ec050e2655fb271e59af91d8148b 100644 (file)
@@ -374,6 +374,27 @@ fn trans_terminator(&mut self,
                          vec![msg_file_line_col],
                          Some(ErrKind::Math(err.clone())))
                     }
+                    mir::AssertMessage::GeneratorResumedAfterReturn |
+                    mir::AssertMessage::GeneratorResumedAfterPanic => {
+                        let str = if let mir::AssertMessage::GeneratorResumedAfterReturn = *msg {
+                            "generator resumed after completion"
+                        } else {
+                            "generator resumed after panicking"
+                        };
+                        let msg_str = Symbol::intern(str).as_str();
+                        let msg_str = C_str_slice(bcx.ccx, msg_str);
+                        let msg_file_line = C_struct(bcx.ccx,
+                                                     &[msg_str, filename, line],
+                                                     false);
+                        let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line));
+                        let msg_file_line = consts::addr_of(bcx.ccx,
+                                                            msg_file_line,
+                                                            align,
+                                                            "panic_loc");
+                        (lang_items::PanicFnLangItem,
+                         vec![msg_file_line],
+                         None)
+                    }
                 };
 
                 // If we know we always panic, and the error message
@@ -557,6 +578,8 @@ fn trans_terminator(&mut self,
                         destination.as_ref().map(|&(_, target)| (ret_dest, sig.output(), target)),
                         cleanup);
             }
+            mir::TerminatorKind::GeneratorDrop |
+            mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
         }
     }
 
index 53469689bc7ddf20e853e5ff5a3e702cdd26a72c..b43e76df04c3ebec5d03b925de8e7b9490670dcf 100644 (file)
@@ -336,6 +336,9 @@ fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
                             mir::AssertMessage::Math(ref err) => {
                                 ErrKind::Math(err.clone())
                             }
+                            mir::AssertMessage::GeneratorResumedAfterReturn |
+                            mir::AssertMessage::GeneratorResumedAfterPanic =>
+                                span_bug!(span, "{:?} should not appear in constants?", msg),
                         };
 
                         let err = ConstEvalErr { span: span, kind: err };
@@ -579,6 +582,7 @@ fn const_rvalue(&self, rvalue: &mir::Rvalue<'tcx>,
                     }
                     mir::AggregateKind::Adt(..) |
                     mir::AggregateKind::Closure(..) |
+                    mir::AggregateKind::Generator(..) |
                     mir::AggregateKind::Tuple => {
                         Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty)
                     }
index a7f12babb10f96c01f16b81862e1a2acb3931e81..3c38619cf5dd4f069c1a8f5f3b5e1ab2195e7bc5 100644 (file)
@@ -524,15 +524,15 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
             }
 
             // Or is it the closure environment?
-            let (closure_ty, env_ref) = if let ty::TyRef(_, mt) = arg_ty.sty {
-                (mt.ty, true)
-            } else {
-                (arg_ty, false)
+            let (closure_ty, env_ref) = match arg_ty.sty {
+                ty::TyRef(_, mt) | ty::TyRawPtr(mt) => (mt.ty, true),
+                _ => (arg_ty, false)
             };
-            let upvar_tys = if let ty::TyClosure(def_id, substs) = closure_ty.sty {
-                substs.upvar_tys(def_id, tcx)
-            } else {
-                bug!("upvar_decls with non-closure arg0 type `{}`", closure_ty);
+
+            let upvar_tys = match closure_ty.sty {
+                ty::TyClosure(def_id, substs) |
+                ty::TyGenerator(def_id, substs, _) => substs.upvar_tys(def_id, tcx),
+                _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_ty)
             };
 
             // Store the pointer to closure data in an alloca for debuginfo
index 1f6a262162d3950021550cba8a44dce92be8777d..94612ef652331addbcd86ff82fd7377c57f9bf7f 100644 (file)
@@ -125,6 +125,12 @@ fn resolve_associated_item<'a, 'tcx>(
             let substs = tcx.erase_regions(&substs);
             ty::Instance::new(def_id, substs)
         }
+        traits::VtableGenerator(closure_data) => {
+            Instance {
+                def: ty::InstanceDef::Item(closure_data.closure_def_id),
+                substs: closure_data.substs.substs
+            }
+        }
         traits::VtableClosure(closure_data) => {
             let trait_closure_kind = tcx.lang_items.fn_trait_kind(trait_id).unwrap();
             resolve_closure(scx, closure_data.closure_def_id, closure_data.substs,
index b94fd13c3a4a29e84f7fdcfd8d5451a732338601..3841e9bfeb7230bc3aa379b521ffe07e23d01cd5 100644 (file)
@@ -504,6 +504,7 @@ pub fn push_type_name(&self, t: Ty<'tcx>, output: &mut String) {
                     self.push_type_name(sig.output(), output);
                 }
             },
+            ty::TyGenerator(def_id, ref closure_substs, _) |
             ty::TyClosure(def_id, ref closure_substs) => {
                 self.push_def_path(def_id, output);
                 let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id));
index 9f9126ba83a8f19fd59b8f66b5196d3c3982dbb1..38c49833e0d75e305521e4a09e42a32ee436ee0a 100644 (file)
@@ -133,6 +133,11 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
           // fill it in *after* placing it into the type cache.
           adt::incomplete_type_of(cx, t, "closure")
       }
+      ty::TyGenerator(..) => {
+          // Only create the named struct, but don't fill it in. We
+          // fill it in *after* placing it into the type cache.
+          adt::incomplete_type_of(cx, t, "generator")
+      }
 
       ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
       ty::TyRawPtr(ty::TypeAndMut{ty, ..}) => {
@@ -197,7 +202,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
 
     // If this was an enum or struct, fill in the type now.
     match t.sty {
-        ty::TyAdt(..) | ty::TyClosure(..) if !t.is_simd() && !t.is_box() => {
+        ty::TyAdt(..) | ty::TyClosure(..) | ty::TyGenerator(..) if !t.is_simd() && !t.is_box() => {
             adt::finish_type_of(cx, t, &mut llty);
         }
         _ => ()
index 802eee91efcf33d456bc935c0850ff1bafaf09db..7e7e1018f97767c1fce74f79c1b0a38e220bf360 100644 (file)
@@ -32,6 +32,8 @@ pub fn check_expr_closure(&self,
         debug!("check_expr_closure(expr={:?},expected={:?})",
                expr,
                expected);
+        // FIXME: Should we adapt deduce_expectations_from_expected_type to work with
+        // generator traits? It looks like it's conservative to add support for this later.
 
         // It's always helpful for inference if we know the kind of
         // closure sooner rather than later, so first examine the expected
@@ -70,22 +72,29 @@ fn check_closure(&self,
         // inference phase (`upvar.rs`).
         let base_substs = Substs::identity_for_item(self.tcx,
             self.tcx.closure_base_def_id(expr_def_id));
-        let closure_type = self.tcx.mk_closure(expr_def_id,
-            base_substs.extend_to(self.tcx, expr_def_id,
+        let substs = base_substs.extend_to(self.tcx, expr_def_id,
                 |_, _| span_bug!(expr.span, "closure has region param"),
                 |_, _| self.infcx.next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span))
-            )
         );
 
-        debug!("check_closure: expr.id={:?} closure_type={:?}", expr.id, closure_type);
-
         let fn_sig = self.liberate_late_bound_regions(expr_def_id, &sig);
         let fn_sig = self.inh.normalize_associated_types_in(body.value.span,
                                                             body.value.id,
                                                             self.param_env,
                                                             &fn_sig);
 
-        check_fn(self, self.param_env, fn_sig, decl, expr.id, body);
+        let interior = check_fn(self, self.param_env, fn_sig, decl, expr.id, body, true).1;
+
+        if let Some(interior) = interior {
+            let closure_substs = ty::ClosureSubsts {
+                substs: substs,
+            };
+            return self.tcx.mk_generator(expr_def_id, closure_substs, interior);
+        }
+
+        let closure_type = self.tcx.mk_closure(expr_def_id, substs);
+
+        debug!("check_closure: expr.id={:?} closure_type={:?}", expr.id, closure_type);
 
         // Tuple up the arguments and insert the resulting function type into
         // the `closures` table.
diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs
new file mode 100644 (file)
index 0000000..5bfcf77
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use log;
+use rustc::hir::def_id::DefId;
+use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
+use rustc::hir::{self, Body, Pat, PatKind, Expr};
+use rustc::middle::region::{RegionMaps, CodeExtent};
+use rustc::ty::Ty;
+use syntax::ast::NodeId;
+use syntax::codemap::Span;
+use std::rc::Rc;
+use super::FnCtxt;
+use util::nodemap::FxHashSet;
+use util::nodemap::FxHashMap;
+
+struct InteriorVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+    fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
+    cache: FxHashMap<NodeId, Option<Span>>,
+    types: FxHashSet<Ty<'tcx>>,
+    region_maps: Rc<RegionMaps>,
+}
+
+impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> {
+    fn record(&mut self, ty: Ty<'tcx>, scope: Option<CodeExtent>, expr: Option<&'tcx Expr>) {
+        use syntax_pos::DUMMY_SP;
+
+        let live_across_yield = scope.map(|s| {
+            self.fcx.tcx.yield_in_extent(s, &mut self.cache).is_some()
+        }).unwrap_or(true);
+
+        if live_across_yield {
+            if log_enabled!(log::LogLevel::Debug) {
+                if let Some(s) = scope {
+                    let span = s.span(&self.fcx.tcx.hir).unwrap_or(DUMMY_SP);
+                    debug!("type in generator with scope = {:?}, type = {:?}, span = {:?}",
+                           scope,
+                           self.fcx.resolve_type_vars_if_possible(&ty),
+                           span);
+                } else {
+                    debug!("type in generator WITHOUT scope, type = {:?}",
+                           self.fcx.resolve_type_vars_if_possible(&ty));
+                }
+                if let Some(e) = expr {
+                    debug!("type from expression: {:?}, span={:?}", e, e.span);
+                }
+            }
+            self.types.insert(ty);
+        } else {
+            if let Some(e) = expr {
+                debug!("NO type from expression: {:?}, span = {:?}", e, e.span);
+            }
+        }
+    }
+}
+
+pub fn resolve_interior<'a, 'gcx, 'tcx>(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
+                                        def_id: DefId,
+                                        body_id: hir::BodyId,
+                                        witness: Ty<'tcx>) {
+    let body = fcx.tcx.hir.body(body_id);
+    let mut visitor = InteriorVisitor {
+        fcx,
+        types: FxHashSet(),
+        cache: FxHashMap(),
+        region_maps: fcx.tcx.region_maps(def_id),
+    };
+    intravisit::walk_body(&mut visitor, body);
+
+    // Deduplicate types
+    let set: FxHashSet<_> = visitor.types.into_iter()
+        .map(|t| fcx.resolve_type_vars_if_possible(&t))
+        .collect();
+    let types: Vec<_> = set.into_iter().collect();
+
+    let tuple = fcx.tcx.intern_tup(&types, false);
+
+    debug!("Types in generator {:?}, span = {:?}", tuple, body.value.span);
+
+    // Unify the tuple with the witness
+    match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(witness, tuple) {
+        Ok(ok) => fcx.register_infer_ok_obligations(ok),
+        _ => bug!(),
+   }
+}
+
+impl<'a, 'gcx, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'gcx, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_body(&mut self, _body: &'tcx Body) {
+        // Closures inside are not considered part of the generator interior
+    }
+
+    fn visit_pat(&mut self, pat: &'tcx Pat) {
+        if let PatKind::Binding(..) = pat.node {
+            let scope = self.region_maps.var_scope(pat.id);
+            let ty = self.fcx.tables.borrow().pat_ty(pat);
+            self.record(ty, Some(scope), None);
+        }
+
+        intravisit::walk_pat(self, pat);
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr) {
+        let scope = self.region_maps.temporary_scope(expr.id);
+        let ty = self.fcx.tables.borrow().expr_ty_adjusted(expr);
+        self.record(ty, scope, Some(expr));
+
+        intravisit::walk_expr(self, expr);
+    }
+}
index 7bf671d5e9f9376de47ef543d5842d935f692457..6669e75d5a0160a9bb551b15b1f594d47d063306 100644 (file)
@@ -724,6 +724,8 @@ fn assemble_extension_candidates_for_trait(&mut self,
 
             self.assemble_closure_candidates(import_id, trait_def_id, item.clone())?;
 
+            self.assemble_generator_candidates(import_id, trait_def_id, item.clone())?;
+
             self.assemble_projection_candidates(import_id, trait_def_id, item.clone());
 
             self.assemble_where_clause_candidates(import_id, trait_def_id, item.clone());
@@ -863,6 +865,48 @@ fn assemble_closure_candidates(&mut self,
         Ok(())
     }
 
+    fn assemble_generator_candidates(&mut self,
+                                   import_id: Option<ast::NodeId>,
+                                   trait_def_id: DefId,
+                                   item: ty::AssociatedItem)
+                                   -> Result<(), MethodError<'tcx>> {
+        // Check if this is the Generator trait.
+        let tcx = self.tcx;
+        if Some(trait_def_id) != tcx.lang_items.gen_trait() {
+            return Ok(());
+        }
+
+        // Check if there is an generator self-type in the list of receivers.
+        // If so, add "synthetic impls".
+        let steps = self.steps.clone();
+        for step in steps.iter() {
+            match step.self_ty.sty {
+                ty::TyGenerator(..) => (),
+                _ => continue,
+            };
+
+            // create some substitutions for the argument/return type;
+            // for the purposes of our method lookup, we only take
+            // receiver type into account, so we can just substitute
+            // fresh types here to use during substitution and subtyping.
+            let substs = Substs::for_item(self.tcx,
+                                          trait_def_id,
+                                          |def, _| self.region_var_for_def(self.span, def),
+                                          |def, substs| {
+                if def.index == 0 {
+                    step.self_ty
+                } else {
+                    self.type_var_for_def(self.span, def, substs)
+                }
+            });
+
+            let xform_self_ty = self.xform_self_ty(&item, step.self_ty, substs);
+            self.push_inherent_candidate(xform_self_ty, item, TraitCandidate, import_id);
+        }
+
+        Ok(())
+    }
+
     fn assemble_projection_candidates(&mut self,
                                       import_id: Option<ast::NodeId>,
                                       trait_def_id: DefId,
index 697d9de931a86068973df2c5358193846905d77e..b22cb0bafe824e32a2b2b27313e797423208869b 100644 (file)
 mod closure;
 mod callee;
 mod compare_method;
+mod generator_interior;
 mod intrinsic;
 mod op;
 
@@ -205,6 +206,8 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
 
+    deferred_generator_interiors: RefCell<Vec<(hir::BodyId, Ty<'tcx>)>>,
+
     // Anonymized types found in explicit return types and their
     // associated fresh inference variable. Writeback resolves these
     // variables to get the concrete type, which can be used to
@@ -503,6 +506,8 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     ret_coercion: Option<RefCell<DynamicCoerceMany<'gcx, 'tcx>>>,
 
+    yield_ty: Option<Ty<'tcx>>,
+
     ps: RefCell<UnsafetyState>,
 
     /// Whether the last checked node generates a divergence (e.g.,
@@ -606,6 +611,7 @@ fn new(infcx: InferCtxt<'a, 'gcx, 'tcx>, def_id: DefId) -> Self {
             locals: RefCell::new(NodeMap()),
             deferred_call_resolutions: RefCell::new(DefIdMap()),
             deferred_cast_checks: RefCell::new(Vec::new()),
+            deferred_generator_interiors: RefCell::new(Vec::new()),
             anon_types: RefCell::new(NodeMap()),
             implicit_region_bound,
             body_id,
@@ -726,11 +732,19 @@ pub fn provide(providers: &mut Providers) {
         typeck_tables_of,
         has_typeck_tables,
         closure_kind,
+        generator_sig,
         adt_destructor,
         ..*providers
     };
 }
 
+fn generator_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          def_id: DefId)
+                          -> Option<ty::PolyGenSig<'tcx>> {
+    let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
+    tcx.typeck_tables_of(def_id).generator_sigs[&node_id].map(|s| ty::Binder(s))
+}
+
 fn closure_kind<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                           def_id: DefId)
                           -> ty::ClosureKind {
@@ -856,7 +870,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                   param_env,
                                                   &fn_sig);
 
-            check_fn(&inh, param_env, fn_sig, decl, id, body)
+            check_fn(&inh, param_env, fn_sig, decl, id, body, false).0
         } else {
             let fcx = FnCtxt::new(&inh, param_env, body.value.id);
             let expected_type = tcx.type_of(def_id);
@@ -878,6 +892,7 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         fcx.closure_analyze(body);
         fcx.select_obligations_where_possible();
         fcx.check_casts();
+        fcx.resolve_generator_interiors(def_id);
         fcx.select_all_obligations_or_error();
 
         if fn_decl.is_some() {
@@ -971,8 +986,9 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
                             fn_sig: ty::FnSig<'tcx>,
                             decl: &'gcx hir::FnDecl,
                             fn_id: ast::NodeId,
-                            body: &'gcx hir::Body)
-                            -> FnCtxt<'a, 'gcx, 'tcx>
+                            body: &'gcx hir::Body,
+                            can_be_generator: bool)
+                            -> (FnCtxt<'a, 'gcx, 'tcx>, Option<ty::GeneratorInterior<'tcx>>)
 {
     let mut fn_sig = fn_sig.clone();
 
@@ -995,6 +1011,12 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
         fn_sig.abi
     );
 
+    let span = body.value.span;
+
+    if body.is_generator && can_be_generator {
+        fcx.yield_ty = Some(fcx.next_ty_var(TypeVariableOrigin::TypeInference(span)));
+    }
+
     GatherLocalsVisitor { fcx: &fcx, }.visit_body(body);
 
     // Add formal parameters.
@@ -1013,6 +1035,24 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
         fcx.write_ty(arg.id, arg_ty);
     }
 
+    let gen_ty = if can_be_generator && body.is_generator {
+        let gen_sig = ty::GenSig {
+            yield_ty: fcx.yield_ty.unwrap(),
+            return_ty: ret_ty,
+        };
+        inherited.tables.borrow_mut().generator_sigs.insert(fn_id, Some(gen_sig));
+
+        let witness = fcx.next_ty_var(TypeVariableOrigin::MiscVariable(span));
+        fcx.deferred_generator_interiors.borrow_mut().push((body.id(), witness));
+        let interior = ty::GeneratorInterior::new(witness);
+
+        inherited.tables.borrow_mut().generator_interiors.insert(fn_id, interior);
+
+        Some(interior)
+    } else {
+        inherited.tables.borrow_mut().generator_sigs.insert(fn_id, None);
+        None
+    };
     inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
 
     fcx.check_return_expr(&body.value);
@@ -1044,11 +1084,11 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     let mut actual_return_ty = coercion.complete(&fcx);
     if actual_return_ty.is_never() {
         actual_return_ty = fcx.next_diverging_ty_var(
-            TypeVariableOrigin::DivergingFn(body.value.span));
+            TypeVariableOrigin::DivergingFn(span));
     }
-    fcx.demand_suptype(body.value.span, ret_ty, actual_return_ty);
+    fcx.demand_suptype(span, ret_ty, actual_return_ty);
 
-    fcx
+    (fcx, gen_ty)
 }
 
 fn check_struct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -1685,6 +1725,7 @@ pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
             param_env,
             err_count_on_creation: inh.tcx.sess.err_count(),
             ret_coercion: None,
+            yield_ty: None,
             ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
                                                      ast::CRATE_NODE_ID)),
             diverges: Cell::new(Diverges::Maybe),
@@ -2066,6 +2107,13 @@ fn check_casts(&self) {
         }
     }
 
+    fn resolve_generator_interiors(&self, def_id: DefId) {
+        let mut deferred_generator_interiors = self.deferred_generator_interiors.borrow_mut();
+        for (body_id, witness) in deferred_generator_interiors.drain(..) {
+            generator_interior::resolve_interior(self, def_id, body_id, witness);
+        }
+    }
+
     /// Apply "fallbacks" to some types
     /// unconstrained types get replaced with ! or  () (depending on whether
     /// feature(never_type) is enabled), unconstrained ints with i32, and
@@ -3092,8 +3140,8 @@ fn check_tup_field(&self,
 
         if tuple_like {
             type_error_struct!(self.tcx().sess, expr.span, expr_t, E0612,
-                               "attempted out-of-bounds tuple index `{}` on type `{}`",
-                               idx.node, expr_t).emit();
+                "attempted out-of-bounds tuple index `{}` on type `{}`",
+                idx.node, expr_t).emit();
         } else {
             self.no_such_field_err(expr.span, idx.node, expr_t).emit();
         }
@@ -3171,7 +3219,7 @@ fn check_expr_struct_fields(&self,
 
         let adt_ty_hint =
             self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty])
-            .get(0).cloned().unwrap_or(adt_ty);
+                .get(0).cloned().unwrap_or(adt_ty);
         // re-link the regions that EIfEO can erase.
         self.demand_eqtype(span, adt_ty_hint, adt_ty);
 
@@ -3209,10 +3257,10 @@ fn check_expr_struct_fields(&self,
                 error_happened = true;
                 if let Some(_) = variant.find_field_named(field.name.node) {
                     let mut err = struct_span_err!(self.tcx.sess,
-                                                   field.name.span,
-                                                   E0062,
-                                                   "field `{}` specified more than once",
-                                                   field.name.node);
+                                                field.name.span,
+                                                E0062,
+                                                "field `{}` specified more than once",
+                                                field.name.node);
 
                     err.span_label(field.name.span, "used more than once");
 
@@ -3260,15 +3308,15 @@ fn check_expr_struct_fields(&self,
                                         .join(", ");
 
             struct_span_err!(tcx.sess, span, E0063,
-                             "missing field{} {}{} in initializer of `{}`",
+                        "missing field{} {}{} in initializer of `{}`",
                              if remaining_fields.len() == 1 { "" } else { "s" },
-                             remaining_fields_names,
-                             truncated_fields_error,
-                             adt_ty)
-                            .span_label(span, format!("missing {}{}",
-                                        remaining_fields_names,
-                                        truncated_fields_error))
-                            .emit();
+                        remaining_fields_names,
+                        truncated_fields_error,
+                        adt_ty)
+                        .span_label(span, format!("missing {}{}",
+                            remaining_fields_names,
+                            truncated_fields_error))
+                        .emit();
         }
     }
 
@@ -3699,13 +3747,10 @@ fn check_expr_kind(&self,
                     // Only check this if not in an `if` condition, as the
                     // mistyped comparison help is more appropriate.
                     if !self.tcx.expr_is_lval(&lhs) {
-                        struct_span_err!(
-                            self.tcx.sess, expr.span, E0070,
-                            "invalid left-hand side expression")
-                        .span_label(
-                            expr.span,
-                            "left-hand of expression not valid")
-                        .emit();
+                        struct_span_err!(self.tcx.sess, expr.span, E0070,
+                                         "invalid left-hand side expression")
+                            .span_label(expr.span, "left-hand of expression not valid")
+                            .emit();
                     }
                 }
             }
@@ -3780,7 +3825,7 @@ fn check_expr_kind(&self,
           hir::ExprMatch(ref discrim, ref arms, match_src) => {
             self.check_match(expr, &discrim, arms, expected, match_src)
           }
-          hir::ExprClosure(capture, ref decl, body_id, _) => {
+          hir::ExprClosure(capture, ref decl, body_id, _, _) => {
               self.check_expr_closure(expr, capture, &decl, body_id, expected)
           }
           hir::ExprBlock(ref body) => {
@@ -3971,6 +4016,18 @@ fn check_expr_kind(&self,
                   }
               }
            }
+          hir::ExprYield(ref value) => {
+            match self.yield_ty {
+                Some(ty) => {
+                    self.check_expr_coercable_to_type(&value, ty);
+                }
+                None => {
+                    struct_span_err!(self.tcx.sess, expr.span, E0627,
+                                 "yield statement outside of generator literal").emit();
+                }
+            }
+            tcx.mk_nil()
+          }
         }
     }
 
index 9b7ecc194ca881b6e2810fe1cc9ccccb927ea3bb..3a39800ea912ffb5125c758d29020be573ccc9d7 100644 (file)
@@ -823,7 +823,7 @@ fn visit_expr(&mut self, expr: &'gcx hir::Expr) {
                 intravisit::walk_expr(self, expr);
             }
 
-            hir::ExprClosure(.., body_id, _) => {
+            hir::ExprClosure(.., body_id, _, _) => {
                 self.check_expr_fn_block(expr, body_id);
             }
 
index 59ca896b347f1fec8428be0758151c1c089a51c8..70325ee3a933346893bd285c6dcb2fac7f196338 100644 (file)
@@ -75,10 +75,11 @@ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
 
     fn visit_expr(&mut self, expr: &'gcx hir::Expr) {
         match expr.node {
-            hir::ExprClosure(cc, _, body_id, _) => {
+            hir::ExprClosure(cc, _, body_id, _, is_generator) => {
                 let body = self.fcx.tcx.hir.body(body_id);
                 self.visit_body(body);
-                self.fcx.analyze_closure(expr.id, expr.span, body, cc);
+                self.fcx.analyze_closure(expr.id, expr.span, body, cc,
+                                         is_generator);
             }
 
             _ => { }
@@ -93,19 +94,22 @@ fn analyze_closure(&self,
                        id: ast::NodeId,
                        span: Span,
                        body: &hir::Body,
-                       capture_clause: hir::CaptureClause) {
+                       capture_clause: hir::CaptureClause,
+                       gen: bool) {
         /*!
          * Analysis starting point.
          */
 
         debug!("analyze_closure(id={:?}, body.id={:?})", id, body.id());
 
-        let infer_kind = match self.tables.borrow_mut().closure_kinds.entry(id) {
-            Entry::Occupied(_) => false,
-            Entry::Vacant(entry) => {
-                debug!("check_closure: adding closure {:?} as Fn", id);
-                entry.insert((ty::ClosureKind::Fn, None));
-                true
+        let infer_kind = if gen { false } else {
+            match self.tables.borrow_mut().closure_kinds.entry(id) {
+                Entry::Occupied(_) => false,
+                Entry::Vacant(entry) => {
+                    debug!("check_closure: adding closure {:?} as Fn", id);
+                    entry.insert((ty::ClosureKind::Fn, None));
+                    true
+                }
             }
         };
 
@@ -173,7 +177,8 @@ fn analyze_closure(&self,
 
         // Extract the type variables UV0...UVn.
         let (def_id, closure_substs) = match self.node_ty(id).sty {
-            ty::TyClosure(def_id, substs) => (def_id, substs),
+            ty::TyClosure(def_id, substs) |
+            ty::TyGenerator(def_id, substs, _) => (def_id, substs),
             ref t => {
                 span_bug!(
                     span,
index 9e5cf5137c21f9f18c66d153c40f7e2cba5b620c..c18a070300a23c07a90063db44d883996ae80816 100644 (file)
@@ -44,6 +44,8 @@ pub fn resolve_type_vars_in_body(&self, body: &'gcx hir::Body)
         wbcx.visit_anon_types();
         wbcx.visit_cast_types();
         wbcx.visit_free_region_map();
+        wbcx.visit_generator_sigs();
+        wbcx.visit_generator_interiors();
 
         let used_trait_imports = mem::replace(&mut self.tables.borrow_mut().used_trait_imports,
                                               DefIdSet());
@@ -159,8 +161,9 @@ fn visit_expr(&mut self, e: &'gcx hir::Expr) {
 
         self.visit_node_id(e.span, e.id);
 
-        if let hir::ExprClosure(_, _, body, _) = e.node {
+        if let hir::ExprClosure(_, _, body, _, _) = e.node {
             let body = self.fcx.tcx.hir.body(body);
+            // FIXME: Why visit the args here?
             for arg in &body.arguments {
                 self.visit_node_id(e.span, arg.id);
             }
@@ -316,6 +319,23 @@ fn visit_adjustments(&mut self, span: Span, node_id: ast::NodeId) {
         }
     }
 
+    fn visit_generator_interiors(&mut self) {
+        for (&node_id, interior) in self.fcx.tables.borrow().generator_interiors.iter() {
+            let interior = self.resolve(interior, &node_id);
+            self.tables.generator_interiors.insert(node_id, interior);
+        }
+    }
+
+    fn visit_generator_sigs(&mut self) {
+        for (&node_id, gen_sig) in self.fcx.tables.borrow().generator_sigs.iter() {
+            let gen_sig = gen_sig.map(|s| ty::GenSig {
+                yield_ty: self.resolve(&s.yield_ty, &node_id),
+                return_ty: self.resolve(&s.return_ty, &node_id),
+            });
+            self.tables.generator_sigs.insert(node_id, gen_sig);
+        }
+    }
+
     fn visit_liberated_fn_sigs(&mut self) {
         for (&node_id, fn_sig) in self.fcx.tables.borrow().liberated_fn_sigs.iter() {
             let fn_sig = self.resolve(fn_sig, &node_id);
index fba32dbb889d9f195c5e1630019365862271ce2c..0ce5c96da7639859aedac65c926737d45b02b83f 100644 (file)
@@ -1153,7 +1153,11 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         NodeField(field) => icx.to_ty(&field.ty),
 
-        NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => {
+        NodeExpr(&hir::Expr { node: hir::ExprClosure(.., is_generator), .. }) => {
+            if is_generator {
+                return tcx.typeck_tables_of(def_id).node_id_to_type(node_id);
+            }
+
             tcx.mk_closure(def_id, Substs::for_item(
                 tcx, def_id,
                 |def, _| {
index c74dc24ddc3406583bbaa96eb2406787508ffff8..6f7e2c80112fccf935a5fba177ec87a3b9d46eaa 100644 (file)
@@ -4774,4 +4774,5 @@ pub fn method(&self) {} // It's now public.
     E0588, // packed struct cannot transitively contain a `[repr(align)]` struct
     E0592, // duplicate definitions with name `{}`
 //  E0613, // Removed (merged with E0609)
+    E0627, // yield statement outside of generator literal
 }
index 284c9c5cfc398aa6572fc14992d229592b05fb70..aa334bc570a768a238b5ae6264f638e27ab250eb 100644 (file)
@@ -294,6 +294,7 @@ fn add_constraints_from_ty(&mut self,
             }
 
             ty::TyFnDef(..) |
+            ty::TyGenerator(..) |
             ty::TyClosure(..) => {
                 bug!("Unexpected closure type in variance computation");
             }
index a9636c7e2fd739027ac498d33bc0e4592300a9e5..75ae2f9fe9ad64107307bb31c2e9bfab079dcd99 100644 (file)
@@ -1943,7 +1943,7 @@ fn clean(&self, cx: &DocContext) -> Type {
                 }).collect())
             }
 
-            ty::TyClosure(..) => Tuple(vec![]), // FIXME(pcwalton)
+            ty::TyClosure(..) | ty::TyGenerator(..) => Tuple(vec![]), // FIXME(pcwalton)
 
             ty::TyInfer(..) => panic!("TyInfer"),
             ty::TyError => panic!("TyError"),
index 857d9a753cc4e8c9de9181d26176e3392a97b04e..cce428cad6df0344fb7dc4f38a8ab544fd94aa8b 100644 (file)
@@ -990,6 +990,9 @@ pub enum ExprKind {
 
     /// `expr?`
     Try(P<Expr>),
+
+    /// A `yield`, with an optional value to be yielded
+    Yield(Option<P<Expr>>),
 }
 
 /// The explicit Self type in a "qualified path". The actual
index aeb574bc3af3cea34236dd90cb533643afc20ddc..cb0cc2aafb074db7ffdae7cb1d32e60fd45e8b89 100644 (file)
@@ -361,6 +361,10 @@ pub fn new() -> Features {
     // Allows unsized tuple coercion.
     (active, unsized_tuple_coercion, "1.20.0", Some(42877)),
 
+    // Generators
+    (active, generators, "1.21.0", None),
+
+
     // global allocators and their internals
     (active, global_allocator, "1.20.0", None),
     (active, allocator_internals, "1.20.0", None),
@@ -1326,6 +1330,11 @@ fn visit_expr(&mut self, e: &'a ast::Expr) {
             ast::ExprKind::InPlace(..) => {
                 gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN);
             }
+            ast::ExprKind::Yield(..) => {
+                gate_feature_post!(&self, generators,
+                                  e.span,
+                                  "yield syntax is experimental");
+            }
             ast::ExprKind::Lit(ref lit) => {
                 if let ast::LitKind::Int(_, ref ty) = lit.node {
                     match *ty {
index a54e2573af40a7fe2e34858f94ec6146113139b3..8f9f179f08b5dca49b8289cf7eae1c86ac2e5574 100644 (file)
@@ -1311,6 +1311,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
                     attrs: fold_attrs(attrs.into(), folder).into(),
                 };
             }
+            ExprKind::Yield(ex) => ExprKind::Yield(ex.map(|x| folder.fold_expr(x))),
             ExprKind::Try(ex) => ExprKind::Try(folder.fold_expr(ex)),
             ExprKind::Catch(body) => ExprKind::Catch(folder.fold_block(body)),
         },
index 7bf4c6799b3cb208119bbaccb6535c643788b051..df1bae523a5c9492681f4dc974ef648540bbf68f 100644 (file)
@@ -2219,6 +2219,14 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
                     };
                     ex = ExprKind::Break(lt, e);
                     hi = self.prev_span;
+                } else if self.eat_keyword(keywords::Yield) {
+                    if self.token.can_begin_expr() {
+                        let e = self.parse_expr()?;
+                        hi = e.span;
+                        ex = ExprKind::Yield(Some(e));
+                    } else {
+                        ex = ExprKind::Yield(None);
+                    }
                 } else if self.token.is_keyword(keywords::Let) {
                     // Catch this syntax error here, instead of in `parse_ident`, so
                     // that we can explicitly mention that let is not to be used as an expression
index 834ac38af9870473db68bf4f2a3df298d655851e..d39f11bc3eef163af0a1e5484332f53802101f41 100644 (file)
@@ -106,6 +106,7 @@ fn ident_can_begin_expr(ident: ast::Ident) -> bool {
         keywords::True.name(),
         keywords::Unsafe.name(),
         keywords::While.name(),
+        keywords::Yield.name(),
     ].contains(&ident.name)
 }
 
index e9d11e73837aa7d32c0f9c21fad8207e6741b616..5832cfcdf36324c50a4876c8549f6315b380314a 100644 (file)
@@ -2281,6 +2281,16 @@ fn print_expr_outer_attr_style(&mut self,
                 self.print_expr(e)?;
                 self.pclose()?;
             },
+            ast::ExprKind::Yield(ref e) => {
+                self.s.word("yield")?;
+                match *e {
+                    Some(ref expr) => {
+                        self.s.space()?;
+                        self.print_expr(&expr)?;
+                    }
+                    _ => ()
+                }
+            }
             ast::ExprKind::Try(ref e) => {
                 self.print_expr(e)?;
                 self.s.word("?")?
index f4ac7e341ce4b6773d201dd38ad90406f570a671..05077d42a0bedb6886ee60069cc03490f6513c26 100644 (file)
@@ -784,6 +784,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
                 visitor.visit_expr(&output.expr)
             }
         }
+        ExprKind::Yield(ref optional_expression) => {
+            walk_list!(visitor, visit_expr, optional_expression);
+        }
         ExprKind::Try(ref subexpression) => {
             visitor.visit_expr(subexpression)
         }
diff --git a/src/test/compile-fail/feature-gate-generators.rs b/src/test/compile-fail/feature-gate-generators.rs
new file mode 100644 (file)
index 0000000..3754f92
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    yield true; //~ ERROR yield syntax is experimental
+}
diff --git a/src/test/run-pass/generator/auxiliary/xcrate.rs b/src/test/run-pass/generator/auxiliary/xcrate.rs
new file mode 100644 (file)
index 0000000..f6878e6
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait, conservative_impl_trait)]
+
+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 = ()>> {
+    Box::new(|| {
+        yield t;
+    })
+}
diff --git a/src/test/run-pass/generator/conditional-drop.rs b/src/test/run-pass/generator/conditional-drop.rs
new file mode 100644 (file)
index 0000000..8329684
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::Generator;
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+
+static A: AtomicUsize = ATOMIC_USIZE_INIT;
+
+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);
+    a.resume();
+    assert_eq!(A.load(Ordering::SeqCst), n + 1);
+    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);
+    a.resume();
+    assert_eq!(A.load(Ordering::SeqCst), n);
+    a.resume();
+    assert_eq!(A.load(Ordering::SeqCst), n + 1);
+}
diff --git a/src/test/run-pass/generator/control-flow.rs b/src/test/run-pass/generator/control-flow.rs
new file mode 100644 (file)
index 0000000..60a00b4
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+
+fn finish<T>(mut amt: usize, mut t: T) -> T::Return
+    where T: Generator<Yield = ()>
+{
+    loop {
+        match 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/run-pass/generator/drop-env.rs b/src/test/run-pass/generator/drop-env.rs
new file mode 100644 (file)
index 0000000..ac42a25
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::Generator;
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+
+static A: AtomicUsize = ATOMIC_USIZE_INIT;
+
+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(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(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/run-pass/generator/iterator-count.rs b/src/test/run-pass/generator/iterator-count.rs
new file mode 100644 (file)
index 0000000..9afe95f
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait, conservative_impl_trait)]
+
+use std::ops::{GeneratorState, Generator};
+
+struct W<T>(T);
+
+impl<T: Generator<Return = ()>> Iterator for W<T> {
+    type Item = T::Yield;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.0.resume() {
+            GeneratorState::Complete(..) => None,
+            GeneratorState::Yielded(v) => Some(v),
+        }
+    }
+}
+
+fn test() -> impl Generator<Return=(), Yield=u8> {
+    || {
+        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/run-pass/generator/panic-drops.rs b/src/test/run-pass/generator/panic-drops.rs
new file mode 100644 (file)
index 0000000..53cd323
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::Generator;
+use std::panic;
+use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+
+static A: AtomicUsize = ATOMIC_USIZE_INIT;
+
+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(|| {
+        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(|| {
+        foo.resume()
+    }));
+    assert!(res.is_err());
+    assert_eq!(A.load(Ordering::SeqCst), 1);
+}
diff --git a/src/test/run-pass/generator/panic-safe.rs b/src/test/run-pass/generator/panic-safe.rs
new file mode 100644 (file)
index 0000000..a583f42
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::Generator;
+use std::panic;
+
+fn main() {
+    let mut foo = || {
+        if true {
+            panic!();
+        }
+        yield;
+    };
+
+    let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+        foo.resume()
+    }));
+    assert!(res.is_err());
+
+    for _ in 0..10 {
+        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+            foo.resume()
+        }));
+        assert!(res.is_err());
+    }
+}
diff --git a/src/test/run-pass/generator/resume-after-return.rs b/src/test/run-pass/generator/resume-after-return.rs
new file mode 100644 (file)
index 0000000..b2e2a3e
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+use std::panic;
+
+fn main() {
+    let mut foo = || {
+        if true {
+            return
+        }
+        yield;
+    };
+
+    match foo.resume() {
+        GeneratorState::Complete(()) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+
+    match panic::catch_unwind(move || foo.resume()) {
+        Ok(_) => panic!("generator successfully resumed"),
+        Err(_) => {}
+    }
+}
diff --git a/src/test/run-pass/generator/smoke.rs b/src/test/run-pass/generator/smoke.rs
new file mode 100644 (file)
index 0000000..48f1b68
--- /dev/null
@@ -0,0 +1,180 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --test
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+use std::thread;
+
+#[test]
+fn simple() {
+    let mut foo = || {
+        if false {
+            yield;
+        }
+    };
+
+    match 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 foo.resume() {
+        GeneratorState::Complete(ref s) if *s == "foo" => {}
+        s => panic!("bad state: {:?}", s),
+    }
+}
+
+#[test]
+fn simple_yield() {
+    let mut foo = || {
+        yield;
+    };
+
+    match foo.resume() {
+        GeneratorState::Yielded(()) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+    match foo.resume() {
+        GeneratorState::Complete(()) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+}
+
+#[test]
+fn yield_capture() {
+    let b = String::from("foo");
+    let mut foo = || {
+        yield b;
+    };
+
+    match foo.resume() {
+        GeneratorState::Yielded(ref s) if *s == "foo" => {}
+        s => panic!("bad state: {:?}", s),
+    }
+    match 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 foo.resume() {
+        GeneratorState::Yielded(ref s) if *s == "bar" => {}
+        s => panic!("bad state: {:?}", s),
+    }
+    match 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 foo.resume() {
+        GeneratorState::Yielded(()) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+    match 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 foo.resume() {
+            GeneratorState::Yielded(()) => {}
+            s => panic!("bad state: {:?}", s),
+        }
+        match 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 foo.resume() {
+            GeneratorState::Yielded(ref s) if *s == "a" => {}
+            s => panic!("bad state: {:?}", s),
+        }
+        match foo.resume() {
+            GeneratorState::Complete(()) => {}
+            s => panic!("bad state: {:?}", s),
+        }
+    }).join().unwrap();
+}
diff --git a/src/test/run-pass/generator/xcrate.rs b/src/test/run-pass/generator/xcrate.rs
new file mode 100644 (file)
index 0000000..dc7a6fd
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:xcrate.rs
+
+#![feature(generators, generator_trait)]
+
+extern crate xcrate;
+
+use std::ops::{GeneratorState, Generator};
+
+fn main() {
+    let mut foo = xcrate::foo();
+
+    match foo.resume() {
+        GeneratorState::Complete(()) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+
+    let mut foo = xcrate::bar(3);
+
+    match foo.resume() {
+        GeneratorState::Yielded(3) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+    match foo.resume() {
+        GeneratorState::Complete(()) => {}
+        s => panic!("bad state: {:?}", s),
+    }
+}
diff --git a/src/test/run-pass/generator/yield-subtype.rs b/src/test/run-pass/generator/yield-subtype.rs
new file mode 100644 (file)
index 0000000..5ff070f
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+fn bar<'a>() {
+    let a: &'static str = "hi";
+    let b: &'a str = a;
+
+    || {
+        yield a;
+        yield b;
+    };
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/ui/generator/borrowing.rs b/src/test/ui/generator/borrowing.rs
new file mode 100644 (file)
index 0000000..de10bde
--- /dev/null
@@ -0,0 +1,30 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::Generator;
+
+fn main() {
+    let _b = {
+        let a = 3;
+        (|| 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 (file)
index 0000000..0ed7e1f
--- /dev/null
@@ -0,0 +1,29 @@
+error[E0597]: `a` does not live long enough
+  --> $DIR/borrowing.rs:18:20
+   |
+18 |         (|| yield &a).resume()
+   |          --        ^ does not live long enough
+   |          |
+   |          capture occurs here
+19 |         //~^ ERROR: `a` does not live long enough
+20 |     };
+   |     - borrowed value only lives until here
+...
+29 | }
+   | - borrowed value needs to live until here
+
+error[E0597]: `a` does not live long enough
+  --> $DIR/borrowing.rs:25:20
+   |
+24 |         || {
+   |         -- capture occurs here
+25 |             yield &a
+   |                    ^ does not live long enough
+...
+28 |     };
+   |     - borrowed value only lives until here
+29 | }
+   | - borrowed value needs to live until here
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/generator/no-arguments-on-generators.rs b/src/test/ui/generator/no-arguments-on-generators.rs
new file mode 100644 (file)
index 0000000..a7e98fe
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+fn main() {
+    let gen = |start| { //~ ERROR generators cannot have explicit arguments
+        yield;
+    };
+}
diff --git a/src/test/ui/generator/no-arguments-on-generators.stderr b/src/test/ui/generator/no-arguments-on-generators.stderr
new file mode 100644 (file)
index 0000000..290ad7b
--- /dev/null
@@ -0,0 +1,8 @@
+error[E0625]: generators cannot have explicit arguments
+  --> $DIR/no-arguments-on-generators.rs:14:15
+   |
+14 |     let gen = |start| { //~ ERROR generators cannot have explicit arguments
+   |               ^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/generator/not-send-sync.rs b/src/test/ui/generator/not-send-sync.rs
new file mode 100644 (file)
index 0000000..0419758
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+use std::cell::Cell;
+
+fn main() {
+    fn assert_sync<T: Sync>(_: T) {}
+    fn assert_send<T: Send>(_: T) {}
+
+    assert_sync(|| {
+        //~^ ERROR: Sync` is not satisfied
+        let a = Cell::new(2);
+        yield;
+    });
+
+    let a = Cell::new(2);
+    assert_send(|| {
+        //~^ ERROR: Sync` is not satisfied
+        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 (file)
index 0000000..a1f110a
--- /dev/null
@@ -0,0 +1,24 @@
+error[E0277]: the trait bound `std::cell::Cell<i32>: std::marker::Sync` is not satisfied
+  --> $DIR/not-send-sync.rs:26:5
+   |
+26 |     assert_send(|| {
+   |     ^^^^^^^^^^^ `std::cell::Cell<i32>` cannot be shared between threads safely
+   |
+   = help: the trait `std::marker::Sync` is not implemented for `std::cell::Cell<i32>`
+   = note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::Cell<i32>`
+   = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:26:17: 30:6 a:&std::cell::Cell<i32> _]`
+   = note: required by `main::assert_send`
+
+error[E0277]: the trait bound `std::cell::Cell<i32>: std::marker::Sync` is not satisfied in `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell<i32>, ())]`
+  --> $DIR/not-send-sync.rs:19:5
+   |
+19 |     assert_sync(|| {
+   |     ^^^^^^^^^^^ `std::cell::Cell<i32>` cannot be shared between threads safely
+   |
+   = help: within `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell<i32>, ())]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<i32>`
+   = note: required because it appears within the type `(std::cell::Cell<i32>, ())`
+   = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell<i32>, ())]`
+   = note: required by `main::assert_sync`
+
+error: aborting due to 2 previous errors
+
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 (file)
index 0000000..87edbb2
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+use std::cell::Cell;
+
+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
+    };
+}
+
+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 (file)
index 0000000..e30d28c
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0597]: `b` does not live long enough
+  --> $DIR/ref-escapes-but-not-over-yield.rs:25:5
+   |
+24 |         a = &b; //~ ERROR
+   |              - borrow occurs here
+25 |     };
+   |     ^ `b` dropped here while still borrowed
+26 | }
+   | - borrowed value needs to live until here
+
+error: aborting due to previous error
+
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 (file)
index 0000000..fb0e681
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+fn foo(_a: (), _b: &bool) {}
+
+// Some examples that probably *could* be accepted, but which we reject for now.
+
+fn bar() {
+    || {
+        let b = true;
+        foo(yield, &b); //~ ERROR
+    };
+}
+
+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 (file)
index 0000000..157f896
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0626]: borrow may still be in use when generator yields
+  --> $DIR/yield-in-args-rev.rs:20:21
+   |
+20 |         foo(yield, &b); //~ ERROR
+   |             -----   ^
+   |             |
+   |             possible yield occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/generator/yield-in-args.rs b/src/test/ui/generator/yield-in-args.rs
new file mode 100644 (file)
index 0000000..faeb4b1
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![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 (file)
index 0000000..0656185
--- /dev/null
@@ -0,0 +1,8 @@
+error[E0626]: borrow may still be in use when generator yields
+  --> $DIR/yield-in-args.rs:18:14
+   |
+18 |         foo(&b, yield); //~ ERROR
+   |              ^  ----- possible yield occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/generator/yield-in-const.rs b/src/test/ui/generator/yield-in-const.rs
new file mode 100644 (file)
index 0000000..e166d26
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+const A: u8 = { yield 3u8; 3u8};
+//~^ ERROR yield statement outside
diff --git a/src/test/ui/generator/yield-in-const.stderr b/src/test/ui/generator/yield-in-const.stderr
new file mode 100644 (file)
index 0000000..8a265c0
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0601]: main function not found
+
+error[E0627]: yield statement outside of generator literal
+  --> $DIR/yield-in-const.rs:13:17
+   |
+13 | const A: u8 = { yield 3u8; 3u8};
+   |                 ^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/generator/yield-in-function.rs b/src/test/ui/generator/yield-in-function.rs
new file mode 100644 (file)
index 0000000..2f6c5a9
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+fn main() { yield; }
+//~^ ERROR yield statement 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 (file)
index 0000000..c6ee3b8
--- /dev/null
@@ -0,0 +1,8 @@
+error[E0627]: yield statement outside of generator literal
+  --> $DIR/yield-in-function.rs:13:13
+   |
+13 | fn main() { yield; }
+   |             ^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/generator/yield-in-static.rs b/src/test/ui/generator/yield-in-static.rs
new file mode 100644 (file)
index 0000000..823a2aa
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+static B: u8 = { yield 3u8; 3u8};
+//~^ ERROR yield statement outside
diff --git a/src/test/ui/generator/yield-in-static.stderr b/src/test/ui/generator/yield-in-static.stderr
new file mode 100644 (file)
index 0000000..d0575a0
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0601]: main function not found
+
+error[E0627]: yield statement outside of generator literal
+  --> $DIR/yield-in-static.rs:13:18
+   |
+13 | static B: u8 = { yield 3u8; 3u8};
+   |                  ^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/generator/yield-while-iterating.rs b/src/test/ui/generator/yield-while-iterating.rs
new file mode 100644 (file)
index 0000000..bc53448
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+use std::cell::Cell;
+
+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;
+        }
+    };
+    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
+    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;
+        }
+    };
+    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 (file)
index 0000000..ea55e03
--- /dev/null
@@ -0,0 +1,24 @@
+error[E0626]: borrow may still be in use when generator yields
+  --> $DIR/yield-while-iterating.rs:22:19
+   |
+22 |         for p in &x { //~ ERROR
+   |                   ^
+23 |             yield();
+   |             ------- possible yield occurs here
+
+error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
+  --> $DIR/yield-while-iterating.rs:67:20
+   |
+62 |     let mut b = || {
+   |                 -- mutable borrow occurs here
+63 |         for p in &mut x {
+   |                       - previous borrow occurs due to use of `x` in closure
+...
+67 |     println!("{}", x[0]); //~ ERROR
+   |                    ^ immutable borrow occurs here
+68 |     b.resume();
+69 | }
+   | - mutable borrow ends here
+
+error: aborting due to 2 previous errors
+
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 (file)
index 0000000..d21c86e
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+use std::cell::Cell;
+
+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 = &3; //~ ERROR
+        yield();
+        println!("{}", a);
+    };
+    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 = &3;
+        }
+        yield();
+    };
+    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
+            yield();
+            println!("{}", b);
+        }
+    };
+    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 (file)
index 0000000..46363e5
--- /dev/null
@@ -0,0 +1,18 @@
+error[E0626]: borrow may still be in use when generator yields
+  --> $DIR/yield-while-local-borrowed.rs:22:18
+   |
+22 |         let a = &3; //~ ERROR
+   |                  ^
+23 |         yield();
+   |         ------- possible yield occurs here
+
+error[E0626]: borrow may still be in use when generator yields
+  --> $DIR/yield-while-local-borrowed.rs:48:22
+   |
+48 |             let b = &a; //~ ERROR
+   |                      ^
+49 |             yield();
+   |             ------- possible yield occurs here
+
+error: aborting due to 2 previous errors
+
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 (file)
index 0000000..b9c963a
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators, generator_trait)]
+
+use std::ops::{GeneratorState, Generator};
+use std::cell::Cell;
+
+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);
+    };
+    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);
+    };
+    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
+    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 (file)
index 0000000..7269f72
--- /dev/null
@@ -0,0 +1,16 @@
+error[E0501]: cannot borrow `x` as immutable because previous closure requires unique access
+  --> $DIR/yield-while-ref-reborrowed.rs:45:20
+   |
+40 |     let mut b = || {
+   |                 -- closure construction occurs here
+41 |         let a = &mut *x;
+   |                       - previous borrow occurs due to use of `x` in closure
+...
+45 |     println!("{}", x); //~ ERROR
+   |                    ^ borrow occurs here
+46 |     b.resume();
+47 | }
+   | - borrow from closure ends here
+
+error: aborting due to previous error
+
index 226fa6469bc740f9403c0f8aa75050a68693574e..281c21f6f85eeee1fd4b9198a4ddfae81615eff5 100644 (file)
@@ -28,11 +28,11 @@ error: expected expression, found `;`
 14 |     foo(bar(;
    |             ^
 
-error: expected one of `)`, `,`, `.`, `<`, `?`, `break`, `continue`, `false`, `for`, `if`, `loop`, `match`, `move`, `return`, `true`, `unsafe`, `while`, or an operator, found `;`
+error: expected one of `)`, `,`, `.`, `<`, `?`, `break`, `continue`, `false`, `for`, `if`, `loop`, `match`, `move`, `return`, `true`, `unsafe`, `while`, `yield`, or an operator, found `;`
   --> $DIR/token-error-correct.rs:14:13
    |
 14 |     foo(bar(;
-   |             ^ expected one of 18 possible tokens here
+   |             ^ expected one of 19 possible tokens here
 
 error: expected expression, found `)`
   --> $DIR/token-error-correct.rs:23:1