--- /dev/null
+# `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.
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;
&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()
+ }
+}
#![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)]
--- /dev/null
+// 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()
+ }
+}
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};
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())
}
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
}
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,
visitor.visit_expr(input)
}
}
+ ExprYield(ref subexpression) => {
+ visitor.visit_expr(subexpression);
+ }
}
}
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,
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)
}
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();
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
{
})))
}
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;
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,
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),
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) => {
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))
}
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)),
}
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())
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)
})
})
}
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) => {
}
},
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"),
},
EntryLifetime(_, n) => NodeLifetime(n),
EntryTyParam(_, n) => NodeTyParam(n),
EntryVisibility(_, n) => NodeVisibility(n),
- _ => return None
+
+ NotPresent |
+ RootCrate => return None
})
}
EntryExpr(_, expr) => {
match expr.node {
- ExprClosure(.., body, _) => Some(body),
+ ExprClosure(.., body, _, _) => Some(body),
_ => None,
}
}
#[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 {
/// 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>),
/// 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.
}
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)?;
self.pclose()?;
}
+ hir::ExprYield(ref expr) => {
+ self.s.word("yield")?;
+ self.print_expr(&expr)?;
+ }
}
self.ann.post(self, NodeExpr(expr))?;
self.end()
hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
+ hir::ExprYield(..) |
hir::ExprInlineAsm(..) |
hir::ExprRepeat(..) |
hir::ExprTup(..) => {
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),
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 {
impl_stable_hash_for!(struct hir::Body {
arguments,
- value
+ value,
+ is_generator
});
impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::BodyId {
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 });
ty,
name,
source_info,
+ internal,
is_user_variable
});
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref });
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,
};
}
mir::TerminatorKind::Resume |
mir::TerminatorKind::Return |
+ mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Unreachable => {}
mir::TerminatorKind::Drop { ref location, target, unwind } => {
location.hash_stable(hcx, hasher);
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,
mir::AssertMessage::Math(ref const_math_err) => {
const_math_err.hash_stable(hcx, hasher);
}
+ mir::AssertMessage::GeneratorResumedAfterReturn => (),
+ mir::AssertMessage::GeneratorResumedAfterPanic => (),
}
}
}
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);
+ }
}
}
}
}
}
+impl_stable_hash_for!(struct ty::GenSig<'tcx> {
+ yield_ty,
+ return_ty
+});
+
impl_stable_hash_for!(struct ty::FnSig<'tcx> {
inputs_and_output,
variadic,
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
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);
ref upvar_capture_map,
ref closure_tys,
ref closure_kinds,
+ ref generator_interiors,
+ ref generator_sigs,
ref liberated_fn_sigs,
ref fru_field_types,
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);
TupleSimplifiedType(size),
TraitSimplifiedType(def_id),
ClosureSimplifiedType(def_id),
+ GeneratorSimplifiedType(def_id),
AnonSimplifiedType(def_id),
FunctionSimplifiedType(params),
ParameterSimplifiedType
ty::TyFnPtr(_) |
ty::TyDynamic(..) |
ty::TyClosure(..) |
+ ty::TyGenerator(..) |
ty::TyNever |
ty::TyTuple(..) |
ty::TyProjection(..) |
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> {
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);
+ }
}
}
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;
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);
}
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)
}
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));
/*
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)
}
}
}
+ 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
}
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);
}
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(..) |
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,
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;
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
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);
}
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,
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;
/// 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`
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>,
visibility_scopes,
promoted,
return_ty,
+ yield_ty,
+ generator_drop: None,
+ generator_layout: None,
local_decls,
arg_count,
upvar_decls,
visibility_scopes,
promoted,
return_ty,
+ yield_ty,
+ generator_drop,
+ generator_layout,
local_decls,
arg_count,
upvar_decls,
/// 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>,
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
}
}
span,
scope: ARGUMENT_VISIBILITY_SCOPE
},
+ internal: false,
name: None, // FIXME maybe we do want some name here?
is_user_variable: false
}
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> {
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(),
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()
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, .. } |
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> {
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, .. } =>
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, ")")
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()
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(_), .. } |
len: Operand<'tcx>,
index: Operand<'tcx>
},
- Math(ConstMathErr)
+ Math(ConstMathErr),
+ GeneratorResumedAfterReturn,
+ GeneratorResumedAfterPanic,
}
///////////////////////////////////////////////////////////////////////////
/// 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)]
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]")
+ }
+ }),
}
}
}
}
}
+/// The layout of generator state
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct GeneratorLayout<'tcx> {
+ pub fields: Vec<LocalDecl<'tcx>>,
+}
/*
* TypeFoldable implementations for MIR types
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(),
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 {
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)
cleanup,
}
},
+ GeneratorDrop => GeneratorDrop,
Resume => Resume,
Return => Return,
Unreachable => Unreachable,
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)
Goto { .. } |
Resume |
Return |
+ GeneratorDrop |
Unreachable => false
}
}
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))
}
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)
}
}
AggregateKind::Closure(did, substs) => {
tcx.mk_closure_from_closure_substs(did, substs)
}
+ AggregateKind::Generator(did, substs, interior) => {
+ tcx.mk_generator(did, substs, interior)
+ }
}
}
}
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 {
match *self {
MirSource::Fn(id) |
MirSource::Const(id) |
+ MirSource::GeneratorDrop(id) |
MirSource::Static(id, _) |
MirSource::Promoted(id, _) => id
}
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;
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) {
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);
TerminatorKind::Resume |
TerminatorKind::Return |
+ TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => {
}
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));
+ }
+
}
}
self.visit_operand(len, location);
self.visit_operand(index, location);
}
- AssertMessage::Math(_) => {}
+ AssertMessage::Math(_) => {},
+ AssertMessage::GeneratorResumedAfterReturn => {},
+ AssertMessage::GeneratorResumedAfterPanic => {},
}
}
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 {
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);
ref $($mutability)* ty,
name: _,
ref $($mutability)* source_info,
+ internal: _,
is_user_variable: _,
} = *local_decl;
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>) {
}
true
}
- ty::TyClosure(..) | ty::TyAnon(..) => {
+ ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAnon(..) => {
bug!("ty_is_local invoked on unexpected type: {:?}", ty)
}
}
AdtKind::Union => Some(16),
AdtKind::Enum => Some(17),
},
+ ty::TyGenerator(..) => Some(18),
ty::TyInfer(..) | ty::TyError => None
}
}
/// 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
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,
VtableBuiltin(i) => i.nested,
VtableDefaultImpl(d) => d.nested,
VtableClosure(c) => c.nested,
+ VtableGenerator(c) => c.nested,
VtableObject(d) => d.nested,
VtableFnPointer(d) => d.nested,
}
&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,
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,
use super::SelectionContext;
use super::SelectionError;
use super::VtableClosureData;
+use super::VtableGeneratorData;
use super::VtableFnPointerData;
use super::VtableImplData;
use super::util;
match vtable {
super::VtableClosure(_) |
+ super::VtableGenerator(_) |
super::VtableFnPointer(_) |
super::VtableObject(_) => {
debug!("assemble_candidates_from_impls: vtable={:?}",
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) =>
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>,
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;
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;
/// 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,
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)
} 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)?;
})
}
+ 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.
///
}
ImplCandidate(..) |
ClosureCandidate(..) |
+ GeneratorCandidate(..) |
FnPointerCandidate |
BuiltinObjectCandidate |
BuiltinUnsizeCandidate |
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
}
ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
- ty::TyClosure(..) |
+ ty::TyClosure(..) | ty::TyGenerator(..) |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
Never
}
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()
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
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,
&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
super::VtableClosure(ref d) =>
write!(f, "{:?}", d),
+ super::VtableGenerator(ref d) =>
+ write!(f, "{:?}", d),
+
super::VtableFnPointer(ref d) =>
write!(f, "VtableFnPointer({:?})", d),
}
}
+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={:?})",
})
}
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,
}
}
+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 {
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))
}
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),
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) => {
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;
/// 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
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(),
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());
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))
}
|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(),
TupleSimplifiedType(usize),
TraitSimplifiedType(DefId),
ClosureSimplifiedType(DefId),
+ GeneratorSimplifiedType(DefId),
AnonSimplifiedType(DefId),
FunctionSimplifiedType(usize),
ParameterSimplifiedType,
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()))
}
}
+ &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);
ty::TyFnDef(def_id, _) |
ty::TyClosure(def_id, _) => Some(def_id),
+ ty::TyGenerator(def_id, _, _) => Some(def_id),
ty::TyBool |
ty::TyChar |
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,
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.
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
}
}
}
/// 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,
use middle::resolve_lifetime::ObjectLifetimeDefault;
use middle::region::CodeExtent;
use mir::Mir;
+use mir::GeneratorLayout;
use traits;
use ty;
use ty::subst::{Subst, Substs};
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};
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
let result = match ty.sty {
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
- TyArray(..) | TyClosure(..) | TyNever => {
+ TyArray(..) | TyClosure(..) | TyGenerator(..) | TyNever => {
vec![]
}
hir::ExprBox(..) |
hir::ExprAddrOf(..) |
hir::ExprBinary(..) |
+ hir::ExprYield(..) |
hir::ExprCast(..) => {
false
}
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> {
}
}
+ 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) => {
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 =>
}
}
+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>,
}
}
+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> {
}
}
+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> {
}
}
+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> {
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)),
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),
}
}
+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);
}
}
+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 {
/// `|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,
}
}
+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
}
}
+#[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.
TyAdt(_, substs) | TyAnon(_, substs) => {
substs.regions().collect()
}
- TyClosure(_, ref substs) => {
+ TyClosure(_, ref substs) | TyGenerator(_, ref substs, _) => {
substs.substs.regions().collect()
}
TyProjection(ref data) => {
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};
}).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
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),
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
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());
}
}
}
- 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.
//
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};
}
}
+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 {
})
}
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")?;
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;
bk={:?}, loan_cause={:?})",
borrow_id, cmt, loan_region,
bk, loan_cause);
-
self.guarantee_valid(borrow_id,
borrow_span,
cmt,
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};
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
{
// 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) =
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
- Some(body),
+ Some(this.body),
cfg,
LoanDataFlowOperator,
id_range,
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,
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())
region_maps: Rc<RegionMaps>,
owner_def_id: DefId,
+
+ body: &'tcx hir::Body,
}
///////////////////////////////////////////////////////////////////////////
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
}
_ => {
_ => { }
}
- 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,
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)"),
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 => {
}
};
- 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) |
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();
}
}
}
}
}
- 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 {
```
"##,
+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! {
// 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;
///
/// 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>,
}
}
+ /// 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())
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,
+ }
+ }
+ }
}
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)>
}
}
+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,
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"));
ty::TyInfer(..) |
ty::TyError |
ty::TyClosure(..) |
+ ty::TyGenerator(..) |
ty::TyProjection(..) |
ty::TyAnon(..) |
ty::TyFnDef(..) => bug!("Unexpected type in foreign function"),
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) }
EntryKind::Impl(_) |
EntryKind::DefaultImpl(_) |
EntryKind::Field |
+ EntryKind::Generator(_) |
EntryKind::Closure(_) => return None,
})
}
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)
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)),
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>>),
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);
}
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 });
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) {
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
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 { .. } |
ExprKind::Borrow { .. } |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
+ ExprKind::Yield { .. } |
ExprKind::InlineAsm { .. } =>
Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::Index { .. } |
ExprKind::Deref { .. } |
ExprKind::Literal { .. } |
+ ExprKind::Yield { .. } |
ExprKind::Field { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
Category::Rvalue(RvalueFunc::Into) => false,
ty: var_ty.clone(),
name: Some(name),
source_info: source_info,
+ internal: false,
is_user_variable: true,
});
self.var_indices.insert(var_id, var);
// 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 }
}
}
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);
});
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)
};
///////////////////////////////////////////////////////////////////////////
// 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> {
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>)>
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());
}).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
}
// 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>,
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> {
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() {
self.visibility_scopes,
IndexVec::new(),
return_ty,
+ yield_ty,
self.local_decls,
self.arg_count,
upvar_decls,
span: pattern.map_or(self.fn_span, |pat| pat.span)
},
name: name,
+ internal: false,
is_user_variable: false,
});
}
/// 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)]
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
}
/// The cached block containing code to run the free. The block will also execute all the drops
/// in the scope.
- cached_block: Option<BasicBlock>
+ cached_block: CachedBlock,
}
#[derive(Clone, Debug)]
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 {
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();
}
}
if let Some(ref mut freedata) = self.free {
- freedata.cached_block = None;
+ freedata.cached_block.invalidate();
}
}
///
/// 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
}
});
if let Some(cached_block) = drops.next() {
Some(cached_block.expect("drop cache is not filled"))
} else if let Some(ref data) = self.free {
- Some(data.cached_block.expect("free cache is not filled"))
+ Some(data.cached_block.get(generator_drop).expect("free cache is not filled"))
} else {
None
}
needs_cleanup: false,
drops: vec![],
free: None,
+ cached_generator_drop: None,
cached_exits: FxHashMap()
});
}
&scope,
&self.scopes,
block,
- self.arg_count));
+ self.arg_count,
+ false));
self.cfg.push_end_region(block, extent.1, scope.extent);
block.unit()
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);
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(span), free);
+ self.cfg.terminate(block, scope.source_info(free_data.span), free);
block = next;
}
}
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;
None,
MirSource::Fn(_) =>
Some(self.topmost_scope()),
- MirSource::Promoted(..) =>
+ MirSource::Promoted(..) |
+ MirSource::GeneratorDrop(..) =>
bug!(),
}
}
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 {
span: span,
value: value.clone(),
item_ty: item_ty,
- cached_block: None
+ cached_block: CachedBlock::default(),
});
return;
}
/// 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;
}
};
for scope in scopes.iter_mut() {
- target = build_diverge_scope(
- hir.tcx(), cfg, &unit_temp, scope.extent_span, scope, target);
+ target = build_diverge_scope(hir.tcx(),
+ cfg,
+ &unit_temp,
+ scope.extent_span,
+ scope,
+ target,
+ generator_drop);
}
Some(target)
}
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();
// 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 {
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 {
unit_temp: &Lvalue<'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
// 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 {
// Next, build up any free.
if let Some(ref mut free_data) = scope.free {
- target = if let Some(cached_block) = free_data.cached_block {
+ target = if let Some(cached_block) = free_data.cached_block.get(generator_drop) {
cached_block
} else {
let into = cfg.start_new_cleanup_block();
cfg.terminate(into, source_info(free_data.span),
build_free(tcx, unit_temp, free_data, target));
- free_data.cached_block = Some(into);
+ *free_data.cached_block.ref_mut(generator_drop) = Some(into);
into
};
}
// 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 {
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 {
match term.kind {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
+ TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
// 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);
}
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);
}
closure_id: def_id,
substs: substs,
upvars: upvars,
+ interior,
}
}
}
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 {
});
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
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())
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;
closure_id: DefId,
substs: ClosureSubsts<'tcx>,
upvars: Vec<ExprRef<'tcx>>,
+ interior: Option<GeneratorInterior<'tcx>>,
},
Literal {
literal: Literal<'tcx>,
outputs: Vec<ExprRef<'tcx>>,
inputs: Vec<ExprRef<'tcx>>
},
+ Yield {
+ value: ExprRef<'tcx>,
+ },
}
#[derive(Clone, Debug)]
LocalDecl {
mutability, ty, name: None,
source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, span },
+ internal: false,
is_user_variable: false
}
}
{
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 {
),
IndexVec::new(),
sig.output(),
+ None,
local_decls_for_sig(&sig, span),
sig.inputs().len(),
vec![],
}
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> {
),
IndexVec::new(),
sig.output(),
+ None,
local_decls,
sig.inputs().len(),
vec![],
),
IndexVec::new(),
sig.output(),
+ None,
local_decls,
sig.inputs().len(),
vec![],
return
}
}
+ MirSource::GeneratorDrop(_) => (),
}
// We only run when the MIR optimization level is > 1.
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);
if !maybe_live {
dead_unwinds.add(&bb);
}
- }
- _ => {}
- }
}
dead_unwinds
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)
});
}
--- /dev/null
+// 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;
+
+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,
+ };
+
+ // 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);
+
+ // 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);
+ }
+}
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[..]);
self.super_terminator_kind(block, kind, loc);
match *kind {
+ TerminatorKind::GeneratorDrop |
+ TerminatorKind::Yield { .. } => bug!(),
TerminatorKind::Goto { ref mut target} => {
*target = self.update_target(*target);
}
pub mod deaggregator;
pub mod instcombine;
pub mod copy_prop;
+pub mod generator;
pub mod inline;
pub mod nll;
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::Unreachable |
+ TerminatorKind::GeneratorDrop |
+ TerminatorKind::Yield { .. } |
TerminatorKind::SwitchInt { .. } => {
/* nothing to do */
},
}).into_iter().collect(),
IndexVec::new(),
ty,
+ None,
initial_locals,
0,
vec![],
TerminatorKind::SwitchInt {..} |
TerminatorKind::DropAndReplace { .. } |
TerminatorKind::Resume |
+ TerminatorKind::GeneratorDrop |
+ TerminatorKind::Yield { .. } |
TerminatorKind::Unreachable => None,
TerminatorKind::Return => {
}
MirSource::Static(_, hir::MutImmutable) => Mode::Static,
MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
+ MirSource::GeneratorDrop(_) |
MirSource::Const(_) |
MirSource::Promoted(..) => return
};
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)*)))
})
}
cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
last_span: Span,
+ body_id: ast::NodeId,
errors_reported: bool
}
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,
})
}
}
+ 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),
TerminatorKind::Goto { .. } |
TerminatorKind::Resume |
TerminatorKind::Return |
+ TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::Drop { .. } => {
// no checks needed for these
}
}
}
+ 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);
+ }
+ }
+ }
+ }
}
}
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, .. } |
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)
}
--- /dev/null
+// 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(())
+}
+
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};
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);
mir: &Mir<'tcx>) {
let promotion_id = match source {
MirSource::Promoted(_, id) => format!("-{:?}", id),
+ MirSource::GeneratorDrop(_) => format!("-drop"),
_ => String::new()
};
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(())
}
/// 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)
/// 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)
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)
+ }
}
}
hir::ExprAgain(_) |
hir::ExprRet(_) |
+ // Generator expressions
+ hir::ExprYield(_) |
+
// Expressions with side-effects.
hir::ExprAssign(..) |
hir::ExprAssignOp(..) |
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) => {
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);
}
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);
}
AggregateKind::Tuple => "AggregateKind::Tuple",
AggregateKind::Adt(..) => "AggregateKind::Adt",
AggregateKind::Closure(..) => "AggregateKind::Closure",
+ AggregateKind::Generator(..) => "AggregateKind::Generator",
}, kind);
"Rvalue::Aggregate"
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)
}
}
mir::TerminatorKind::Return |
mir::TerminatorKind::Unreachable |
mir::TerminatorKind::Assert { .. } => {}
+ mir::TerminatorKind::GeneratorDrop |
+ mir::TerminatorKind::Yield { .. } => bug!(),
}
self.super_terminator_kind(block, kind, location);
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;
}
}))
}
+ 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;
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)
}
}
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,
ty::TyClosure(..) => {
output.push_str("closure");
}
+ ty::TyGenerator(..) => {
+ output.push_str("generator");
+ }
ty::TyError |
ty::TyInfer(_) |
ty::TyProjection(..) |
TerminatorKind::Goto { .. } |
TerminatorKind::Resume |
TerminatorKind::Return |
+ TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
- TerminatorKind::SwitchInt { .. } => {
+ TerminatorKind::SwitchInt { .. } |
+ TerminatorKind::Yield { .. } => {
/* nothing to do */
}
TerminatorKind::Call { cleanup: unwind, .. } |
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
destination.as_ref().map(|&(_, target)| (ret_dest, sig.output(), target)),
cleanup);
}
+ mir::TerminatorKind::GeneratorDrop |
+ mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"),
}
}
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 };
}
mir::AggregateKind::Adt(..) |
mir::AggregateKind::Closure(..) |
+ mir::AggregateKind::Generator(..) |
mir::AggregateKind::Tuple => {
Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty)
}
}
// 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
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,
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));
// 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, ..}) => {
// 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);
}
_ => ()
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
// 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.
--- /dev/null
+// 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);
+ }
+}
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());
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,
mod closure;
mod callee;
mod compare_method;
+mod generator_interior;
mod intrinsic;
mod op;
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
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.,
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,
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 {
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);
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() {
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();
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.
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);
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>,
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),
}
}
+ 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
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();
}
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);
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");
.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();
}
}
// 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();
}
}
}
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) => {
}
}
}
+ 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()
+ }
}
}
intravisit::walk_expr(self, expr);
}
- hir::ExprClosure(.., body_id, _) => {
+ hir::ExprClosure(.., body_id, _, _) => {
self.check_expr_fn_block(expr, body_id);
}
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);
}
_ => { }
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
+ }
}
};
// 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,
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());
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);
}
}
}
+ 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);
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, _| {
Erroneous code example:
-```compile_fail,E0624
+```compile_fail,E0627
mod inner {
pub struct Foo;
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
}
}
ty::TyFnDef(..) |
+ ty::TyGenerator(..) |
ty::TyClosure(..) => {
bug!("Unexpected closure type in variance computation");
}
}).collect())
}
- ty::TyClosure(..) => Tuple(vec![]), // FIXME(pcwalton)
+ ty::TyClosure(..) | ty::TyGenerator(..) => Tuple(vec![]), // FIXME(pcwalton)
ty::TyInfer(..) => panic!("TyInfer"),
ty::TyError => panic!("TyError"),
/// `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
// 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),
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 {
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)),
},
};
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
keywords::True.name(),
keywords::Unsafe.name(),
keywords::While.name(),
+ keywords::Yield.name(),
].contains(&ident.name)
}
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("?")?
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)
}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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;
+ })
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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
+ });
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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));
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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());
+ }
+}
--- /dev/null
+// 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(_) => {}
+ }
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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),
+ }
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
+ }
+ };
+}
+
--- /dev/null
+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
+
--- /dev/null
+// 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;
+ };
+}
--- /dev/null
+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
+
--- /dev/null
+// 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;
+ });
+}
--- /dev/null
+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
+
--- /dev/null
+// 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() { }
--- /dev/null
+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
+
--- /dev/null
+// 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() { }
--- /dev/null
+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
+
--- /dev/null
+// 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
+ };
+}
--- /dev/null
+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
+
--- /dev/null
+// 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
--- /dev/null
+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
+
--- /dev/null
+// 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
--- /dev/null
+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
+
--- /dev/null
+// 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
--- /dev/null
+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
+
--- /dev/null
+// 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() { }
--- /dev/null
+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
+
--- /dev/null
+// 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() { }
--- /dev/null
+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
+
--- /dev/null
+// 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() { }
--- /dev/null
+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
+
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