Auto merge of #43076 - Zoxc:gen, r=arielb1

Generator support

This adds experimental support for generators intended to land once https://github.com/rust-lang/rfcs/pull/2033 is approved.

This is not yet ready to be merged. Things to do:
- [x] Make closure arguments on generators an error
- [x] Spot FIXMEs
- [x] Pass make tidy
- [x] Write tests
- [x] Document the current syntax and semantics for generators somewhere
- [x] Use proper error message numbers
- [x] ~~Make the implicit argument type default to `()`~~
This commit is contained in:
bors 2017-08-28 16:36:03 +00:00
commit 9a59d69323
164 changed files with 5053 additions and 471 deletions

6
src/Cargo.lock generated
View file

@ -1181,7 +1181,7 @@ dependencies = [
"rls-data 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rls-vfs 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rustfmt-nightly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustfmt-nightly 0.2.2 (git+https://github.com/rust-lang-nursery/rustfmt?branch=yield)",
"serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1648,7 +1648,7 @@ dependencies = [
[[package]]
name = "rustfmt-nightly"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "git+https://github.com/rust-lang-nursery/rustfmt?branch=yield#6f56783c8cfd5f06d9b77873a3d4feb56c8d843f"
dependencies = [
"diff 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2287,7 +2287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rls-vfs 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd34691a510938bb67fe0444fb363103c73ffb31c121d1e16bc92d8945ea8ff"
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustfmt-nightly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6eea0d0590ae793fc4d281df56e01dc7531575c8ed9a72fadf5fdc7305a0d32f"
"checksum rustfmt-nightly 0.2.2 (git+https://github.com/rust-lang-nursery/rustfmt?branch=yield)" = "<none>"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum scopeguard 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59a076157c1e2dc561d8de585151ee6965d910dd4dcb5dabb7ae3e83981a6c57"

View file

@ -0,0 +1,245 @@
# `generators`
The tracking issue for this feature is: [#43122]
[#34511]: https://github.com/rust-lang/rust/issues/43122
------------------------
The `generators` feature gate in Rust allows you to define generator or
coroutine literals. A generator is a "resumable function" that syntactically
resembles a closure but compiles to much different semantics in the compiler
itself. The primary feature of a generator is that it can be suspended during
execution to be resumed at a later date. Generators use the `yield` keyword to
"return", and then the caller can `resume` a generator to resume execution just
after the `yield` keyword.
Generators are an extra-unstable feature in the compiler right now. Added in
[RFC 2033] they're mostly intended right now as a information/constraint
gathering phase. The intent is that experimentation can happen on the nightly
compiler before actual stabilization. A further RFC will be required to
stabilize generators/coroutines and will likely contain at least a few small
tweaks to the overall design.
[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
A syntactical example of a generator is:
```rust
#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
fn main() {
let mut generator = || {
yield 1;
return "foo"
};
match generator.resume() {
GeneratorState::Yielded(1) => {}
_ => panic!("unexpected value from resume"),
}
match generator.resume() {
GeneratorState::Complete("foo") => {}
_ => panic!("unexpected value from resume"),
}
}
```
Generators are closure-like literals which can contain a `yield` statement. The
`yield` statement takes an optional expression of a value to yield out of the
generator. All generator literals implement the `Generator` trait in the
`std::ops` module. The `Generator` trait has one main method, `resume`, which
resumes execution of the generator at the previous suspension point.
An example of the control flow of generators is that the following example
prints all numbers in order:
```rust
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn main() {
let mut generator = || {
println!("2");
yield;
println!("4");
};
println!("1");
generator.resume();
println!("3");
generator.resume();
println!("5");
}
```
At this time the main intended use case of generators is an implementation
primitive for async/await syntax, but generators will likely be extended to
ergonomic implementations of iterators and other primitives in the future.
Feedback on the design and usage is always appreciated!
### The `Generator` trait
The `Generator` trait in `std::ops` currently looks like:
```
# #![feature(generator_trait)]
# use std::ops::GeneratorState;
pub trait Generator {
type Yield;
type Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
}
```
The `Generator::Yield` type is the type of values that can be yielded with the
`yield` statement. The `Generator::Return` type is the returned type of the
generator. This is typically the last expression in a generator's definition or
any value passed to `return` in a generator. The `resume` function is the entry
point for executing the `Generator` itself.
The return value of `resume`, `GeneratorState`, looks like:
```
pub enum GeneratorState<Y, R> {
Yielded(Y),
Complete(R),
}
```
The `Yielded` variant indicates that the generator can later be resumed. This
corresponds to a `yield` point in a generator. The `Complete` variant indicates
that the generator is complete and cannot be resumed again. Calling `resume`
after a generator has returned `Complete` will likely result in a panic of the
program.
### Closure-like semantics
The closure-like syntax for generators alludes to the fact that they also have
closure-like semantics. Namely:
* When created, a generator executes no code. A closure literal does not
actually execute any of the closure's code on construction, and similarly a
generator literal does not execute any code inside the generator when
constructed.
* Generators can capture outer variables by reference or by move, and this can
be tweaked with the `move` keyword at the beginning of the closure. Like
closures all generators will have an implicit environment which is inferred by
the compiler. Outer variables can be moved into a generator for use as the
generator progresses.
* Generator literals produce a value with a unique type which implements the
`std::ops::Generator` trait. This allows actual execution of the generator
through the `Generator::resume` method as well as also naming it in return
types and such.
* Traits like `Send` and `Sync` are automatically implemented for a `Generator`
depending on the captured variables of the environment. Unlike closures though
generators also depend on variables live across suspension points. This means
that although the ambient environment may be `Send` or `Sync`, the generator
itself may not be due to internal variables live across `yield` points being
not-`Send` or not-`Sync`. Note, though, that generators, like closures, do
not implement traits like `Copy` or `Clone` automatically.
* Whenever a generator is dropped it will drop all captured environment
variables.
Note that unlike closures generators at this time cannot take any arguments.
That is, generators must always look like `|| { ... }`. This restriction may be
lifted at a future date, the design is ongoing!
### Generators as state machines
In the compiler generators are currently compiled as state machines. Each
`yield` expression will correspond to a different state that stores all live
variables over that suspension point. Resumption of a generator will dispatch on
the current state and then execute internally until a `yield` is reached, at
which point all state is saved off in the generator and a value is returned.
Let's take a look at an example to see what's going on here:
```rust
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn main() {
let ret = "foo";
let mut generator = move || {
yield 1;
return ret
};
generator.resume();
generator.resume();
}
```
This generator literal will compile down to something similar to:
```rust
#![feature(generators, generator_trait)]
use std::ops::{Generator, GeneratorState};
fn main() {
let ret = "foo";
let mut generator = {
enum __Generator {
Start(&'static str),
Yield1(&'static str),
Done,
}
impl Generator for __Generator {
type Yield = i32;
type Return = &'static str;
fn resume(&mut self) -> GeneratorState<i32, &'static str> {
use std::mem;
match mem::replace(self, __Generator::Done) {
__Generator::Start(s) => {
*self = __Generator::Yield1(s);
GeneratorState::Yielded(1)
}
__Generator::Yield1(s) => {
*self = __Generator::Done;
GeneratorState::Complete(s)
}
__Generator::Done => {
panic!("generator resumed after completion")
}
}
}
}
__Generator::Start(ret)
};
generator.resume();
generator.resume();
}
```
Notably here we can see that the compiler is generating a fresh type,
`__Generator` in this case. This type has a number of states (represented here
as an `enum`) corresponding to each of the conceptual states of the generator.
At the beginning we're closing over our outer variable `foo` and then that
variable is also live over the `yield` point, so it's stored in both states.
When the generator starts it'll immediately yield 1, but it saves off its state
just before it does so indicating that it has reached the yield point. Upon
resuming again we'll execute the `return ret` which returns the `Complete`
state.
Here we can also note that the `Done` state, if resumed, panics immediately as
it's invalid to resume a completed generator. It's also worth noting that this
is just a rough desugaring, not a normative specification for what the compiler
does.

View file

@ -66,7 +66,7 @@ use core::hash::{self, Hash};
use core::iter::FusedIterator;
use core::marker::{self, Unsize};
use core::mem;
use core::ops::{CoerceUnsized, Deref, DerefMut};
use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState};
use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer};
use core::ptr::{self, Unique};
use core::convert::From;
@ -784,3 +784,14 @@ impl<T: ?Sized> AsMut<T> for Box<T> {
&mut **self
}
}
#[unstable(feature = "generator_trait", issue = "43122")]
impl<T> Generator for Box<T>
where T: Generator + ?Sized
{
type Yield = T::Yield;
type Return = T::Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
(**self).resume()
}
}

View file

@ -80,6 +80,7 @@
#![cfg_attr(not(test), feature(core_float))]
#![cfg_attr(not(test), feature(exact_size_is_empty))]
#![cfg_attr(not(test), feature(slice_rotate))]
#![cfg_attr(not(test), feature(generator_trait))]
#![cfg_attr(test, feature(rand, test))]
#![feature(allow_internal_unstable)]
#![feature(box_patterns)]

View file

@ -0,0 +1,131 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// The result of a generator resumption.
///
/// This enum is returned from the `Generator::resume` method and indicates the
/// possible return values of a generator. Currently this corresponds to either
/// a suspension point (`Yielded`) or a termination point (`Complete`).
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[cfg_attr(not(stage0), lang = "generator_state")]
#[unstable(feature = "generator_trait", issue = "43122")]
pub enum GeneratorState<Y, R> {
/// The generator suspended with a value.
///
/// This state indicates that a generator has been suspended, and typically
/// corresponds to a `yield` statement. The value provided in this variant
/// corresponds to the expression passed to `yield` and allows generators to
/// provide a value each time they yield.
Yielded(Y),
/// The generator completed with a return value.
///
/// This state indicates that a generator has finished execution with the
/// provided value. Once a generator has returned `Complete` it is
/// considered a programmer error to call `resume` again.
Complete(R),
}
/// The trait implemented by builtin generator types.
///
/// Generators, also commonly referred to as coroutines, are currently an
/// experimental language feature in Rust. Added in [RFC 2033] generators are
/// currently intended to primarily provide a building block for async/await
/// syntax but will likely extend to also providing an ergonomic definition for
/// iterators and other primitives.
///
/// The syntax and semantics for generators is unstable and will require a
/// further RFC for stabilization. At this time, though, the syntax is
/// closure-like:
///
/// ```rust
/// #![feature(generators, generator_trait)]
///
/// use std::ops::{Generator, GeneratorState};
///
/// fn main() {
/// let mut generator = || {
/// yield 1;
/// return "foo"
/// };
///
/// match generator.resume() {
/// GeneratorState::Yielded(1) => {}
/// _ => panic!("unexpected return from resume"),
/// }
/// match generator.resume() {
/// GeneratorState::Complete("foo") => {}
/// _ => panic!("unexpected return from resume"),
/// }
/// }
/// ```
///
/// More documentation of generators can be found in the unstable book.
///
/// [RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033
#[cfg_attr(not(stage0), lang = "generator")]
#[unstable(feature = "generator_trait", issue = "43122")]
#[fundamental]
pub trait Generator {
/// The type of value this generator yields.
///
/// This associated type corresponds to the `yield` expression and the
/// values which are allowed to be returned each time a generator yields.
/// For example an iterator-as-a-generator would likely have this type as
/// `T`, the type being iterated over.
type Yield;
/// The type of value this generator returns.
///
/// This corresponds to the type returned from a generator either with a
/// `return` statement or implicitly as the last expression of a generator
/// literal. For example futures would use this as `Result<T, E>` as it
/// represents a completed future.
type Return;
/// Resumes the execution of this generator.
///
/// This function will resume execution of the generator or start execution
/// if it hasn't already. This call will return back into the generator's
/// last suspension point, resuming execution from the latest `yield`. The
/// generator will continue executing until it either yields or returns, at
/// which point this function will return.
///
/// # Return value
///
/// The `GeneratorState` enum returned from this function indicates what
/// state the generator is in upon returning. If the `Yielded` variant is
/// returned then the generator has reached a suspension point and a value
/// has been yielded out. Generators in this state are available for
/// resumption at a later point.
///
/// If `Complete` is returned then the generator has completely finished
/// with the value provided. It is invalid for the generator to be resumed
/// again.
///
/// # Panics
///
/// This function may panic if it is called after the `Complete` variant has
/// been returned previously. While generator literals in the language are
/// guaranteed to panic on resuming after `Complete`, this is not guaranteed
/// for all implementations of the `Generator` trait.
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
}
#[unstable(feature = "generator_trait", issue = "43122")]
impl<'a, T> Generator for &'a mut T
where T: Generator + ?Sized
{
type Yield = T::Yield;
type Return = T::Return;
fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return> {
(**self).resume()
}
}

View file

@ -159,6 +159,7 @@ mod bit;
mod deref;
mod drop;
mod function;
mod generator;
mod index;
mod place;
mod range;
@ -196,6 +197,9 @@ pub use self::range::{RangeInclusive, RangeToInclusive};
#[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};

View file

@ -389,6 +389,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
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())
}

View file

@ -434,6 +434,7 @@ define_dep_nodes!( <'tcx>
[] ImplPolarity(DefId),
[] ClosureKind(DefId),
[] FnSignature(DefId),
[] GenSignature(DefId),
[] CoerceUnsizedInfo(DefId),
[] ItemVarianceConstraints(DefId),

View file

@ -2045,4 +2045,5 @@ register_diagnostics! {
E0495, // cannot infer an appropriate lifetime due to conflicting requirements
E0566, // conflicting representation hints
E0623, // lifetime mismatch where both parameters are anonymous regions
E0628, // generators cannot have explicit arguments
}

View file

@ -979,7 +979,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
visitor.visit_expr(subexpression);
walk_list!(visitor, visit_arm, arms);
}
ExprClosure(_, ref function_declaration, body, _fn_decl_span) => {
ExprClosure(_, ref function_declaration, body, _fn_decl_span, _gen) => {
visitor.visit_fn(FnKind::Closure(&expression.attrs),
function_declaration,
body,
@ -1043,6 +1043,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
visitor.visit_expr(input)
}
}
ExprYield(ref subexpression) => {
visitor.visit_expr(subexpression);
}
}
}

View file

@ -93,6 +93,8 @@ pub struct LoweringContext<'a> {
trait_impls: BTreeMap<DefId, Vec<NodeId>>,
trait_default_impl: BTreeMap<DefId, NodeId>,
is_generator: bool,
catch_scopes: Vec<NodeId>,
loop_scopes: Vec<NodeId>,
is_in_loop_condition: bool,
@ -145,6 +147,7 @@ pub fn lower_crate(sess: &Session,
current_hir_id_owner: vec![(CRATE_DEF_INDEX, 0)],
item_local_id_counters: NodeMap(),
node_id_to_hir_id: IndexVec::new(),
is_generator: false,
}.lower_crate(krate)
}
@ -393,6 +396,7 @@ impl<'a> LoweringContext<'a> {
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();
@ -453,6 +457,16 @@ impl<'a> LoweringContext<'a> {
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
{
@ -637,13 +651,12 @@ impl<'a> LoweringContext<'a> {
})))
}
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;
@ -700,8 +713,7 @@ impl<'a> LoweringContext<'a> {
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,
@ -1368,21 +1380,21 @@ impl<'a> LoweringContext<'a> {
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),
@ -1478,8 +1490,7 @@ impl<'a> LoweringContext<'a> {
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) => {
@ -1488,9 +1499,10 @@ impl<'a> LoweringContext<'a> {
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))
}
@ -1542,14 +1554,14 @@ impl<'a> LoweringContext<'a> {
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)),
@ -1928,8 +1940,8 @@ impl<'a> LoweringContext<'a> {
}
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())
@ -2027,11 +2039,22 @@ impl<'a> LoweringContext<'a> {
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, E0628,
"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)
})
})
}
@ -2172,6 +2195,14 @@ impl<'a> LoweringContext<'a> {
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) => {

View file

@ -264,7 +264,7 @@ impl<'a> FnLikeNode<'a> {
}
},
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"),
},

View file

@ -151,7 +151,9 @@ impl<'hir> MapEntry<'hir> {
EntryTyParam(_, _, n) => NodeTyParam(n),
EntryVisibility(_, _, n) => NodeVisibility(n),
EntryLocal(_, _, n) => NodeLocal(n),
_ => return None
NotPresent |
RootCrate(_) => return None
})
}
@ -184,7 +186,7 @@ impl<'hir> MapEntry<'hir> {
EntryExpr(_, _, expr) => {
match expr.node {
ExprClosure(.., body, _) => Some(body),
ExprClosure(.., body, _, _) => Some(body),
_ => None,
}
}

View file

@ -929,7 +929,8 @@ pub struct BodyId {
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct Body {
pub arguments: HirVec<Arg>,
pub value: Expr
pub value: Expr,
pub is_generator: bool,
}
impl Body {
@ -1007,7 +1008,10 @@ pub enum Expr_ {
/// A closure (for example, `move |a, b, c| {a + b + c}`).
///
/// The final span is the span of the argument block `|...|`
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span),
///
/// This may also be a generator literal, indicated by the final boolean,
/// in that case there is an GeneratorClause.
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, bool),
/// A block (`{ ... }`)
ExprBlock(P<Block>),
@ -1052,6 +1056,9 @@ pub enum Expr_ {
/// For example, `[1; 5]`. The first expression is the element
/// to be repeated; the second is the number of times to repeat it.
ExprRepeat(P<Expr>, BodyId),
/// A suspension point for generators. This is `yield <expr>` in Rust.
ExprYield(P<Expr>),
}
/// Optionally `Self`-qualified value/type path or associated extension.

View file

@ -1312,7 +1312,7 @@ impl<'a> State<'a> {
}
self.bclose_(expr.span, indent_unit)?;
}
hir::ExprClosure(capture_clause, ref decl, body, _fn_decl_span) => {
hir::ExprClosure(capture_clause, ref decl, body, _fn_decl_span, _gen) => {
self.print_capture_clause(capture_clause)?;
self.print_closure_args(&decl, body)?;
@ -1461,6 +1461,10 @@ impl<'a> State<'a> {
self.pclose()?;
}
hir::ExprYield(ref expr) => {
self.s.word("yield")?;
self.print_expr(&expr)?;
}
}
self.ann.post(self, NodeExpr(expr))?;
self.end()

View file

@ -569,6 +569,7 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::E
hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
hir::ExprYield(..) |
hir::ExprInlineAsm(..) |
hir::ExprRepeat(..) |
hir::ExprTup(..) => {
@ -633,7 +634,7 @@ impl_stable_hash_for!(enum hir::Expr_ {
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),
@ -647,7 +648,8 @@ impl_stable_hash_for!(enum hir::Expr_ {
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 {
@ -1024,7 +1026,8 @@ impl_stable_hash_for!(struct hir::Arg {
impl_stable_hash_for!(struct hir::Body {
arguments,
value
value,
is_generator
});
impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::BodyId {

View file

@ -17,7 +17,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHasherResult};
use std::mem;
impl_stable_hash_for!(struct mir::GeneratorLayout<'tcx> { fields });
impl_stable_hash_for!(struct mir::SourceInfo { span, scope });
impl_stable_hash_for!(enum mir::Mutability { Mut, Not });
impl_stable_hash_for!(enum mir::BorrowKind { Shared, Unique, Mut });
@ -27,6 +27,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
ty,
name,
source_info,
internal,
is_user_variable
});
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref });
@ -54,9 +55,11 @@ for mir::Terminator<'gcx> {
mir::TerminatorKind::SwitchInt { .. } |
mir::TerminatorKind::Resume |
mir::TerminatorKind::Return |
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Unreachable |
mir::TerminatorKind::Drop { .. } |
mir::TerminatorKind::DropAndReplace { .. } |
mir::TerminatorKind::Yield { .. } |
mir::TerminatorKind::Call { .. } => false,
};
@ -146,6 +149,7 @@ for mir::TerminatorKind<'gcx> {
}
mir::TerminatorKind::Resume |
mir::TerminatorKind::Return |
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Unreachable => {}
mir::TerminatorKind::Drop { ref location, target, unwind } => {
location.hash_stable(hcx, hasher);
@ -161,6 +165,13 @@ for mir::TerminatorKind<'gcx> {
target.hash_stable(hcx, hasher);
unwind.hash_stable(hcx, hasher);
}
mir::TerminatorKind::Yield { ref value,
resume,
drop } => {
value.hash_stable(hcx, hasher);
resume.hash_stable(hcx, hasher);
drop.hash_stable(hcx, hasher);
}
mir::TerminatorKind::Call { ref func,
ref args,
ref destination,
@ -200,6 +211,8 @@ for mir::AssertMessage<'gcx> {
mir::AssertMessage::Math(ref const_math_err) => {
const_math_err.hash_stable(hcx, hasher);
}
mir::AssertMessage::GeneratorResumedAfterReturn => (),
mir::AssertMessage::GeneratorResumedAfterPanic => (),
}
}
}
@ -433,6 +446,11 @@ for mir::AggregateKind<'gcx> {
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);
}
}
}
}

View file

@ -147,6 +147,11 @@ for ty::UpvarCapture<'gcx> {
}
}
impl_stable_hash_for!(struct ty::GenSig<'tcx> {
yield_ty,
return_ty
});
impl_stable_hash_for!(struct ty::FnSig<'tcx> {
inputs_and_output,
variadic,
@ -321,6 +326,8 @@ for ::middle::const_val::ConstVal<'gcx> {
impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs });
impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness });
impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> {
parent,
predicates
@ -546,6 +553,12 @@ for ty::TypeVariants<'gcx>
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);
@ -625,6 +638,7 @@ impl_stable_hash_for!(enum ty::fast_reject::SimplifiedType {
TupleSimplifiedType(size),
TraitSimplifiedType(def_id),
ClosureSimplifiedType(def_id),
GeneratorSimplifiedType(def_id),
AnonSimplifiedType(def_id),
FunctionSimplifiedType(params),
ParameterSimplifiedType

View file

@ -166,6 +166,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
ty::TyFnPtr(_) |
ty::TyDynamic(..) |
ty::TyClosure(..) |
ty::TyGenerator(..) |
ty::TyNever |
ty::TyTuple(..) |
ty::TyProjection(..) |

View file

@ -1363,6 +1363,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.tcx.fn_sig(def_id)
}
pub fn generator_sig(&self, def_id: DefId) -> Option<ty::PolyGenSig<'tcx>> {
if let Some(tables) = self.in_progress_tables {
if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
let hir_id = self.tcx.hir.node_to_hir_id(id);
if let Some(&ty) = tables.borrow().generator_sigs().get(hir_id) {
return ty.map(|t| ty::Binder(t));
}
}
}
self.tcx.generator_sig(def_id)
}
}
impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {

View file

@ -517,13 +517,17 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, '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);
}
}
}

View file

@ -316,6 +316,9 @@ language_item_table! {
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;

View file

@ -460,7 +460,7 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
hir::ExprAgain(_) | hir::ExprLit(_) | hir::ExprRet(..) |
hir::ExprBlock(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) |
hir::ExprStruct(..) | hir::ExprRepeat(..) |
hir::ExprInlineAsm(..) | hir::ExprBox(..) |
hir::ExprInlineAsm(..) | hir::ExprBox(..) | hir::ExprYield(..) |
hir::ExprType(..) | hir::ExprPath(hir::QPath::TypeRelative(..)) => {
intravisit::walk_expr(ir, expr);
}
@ -881,7 +881,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
match expr.node {
// Interesting cases with control flow or which gen/kill
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
self.access_path(expr.id, path, succ, ACC_READ | ACC_USE)
}
@ -894,7 +893,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&e, succ)
}
hir::ExprClosure(.., blk_id, _) => {
hir::ExprClosure(.., blk_id, _, _) => {
debug!("{} is an ExprClosure", self.ir.tcx.hir.node_to_pretty_string(expr.id));
/*
@ -1116,6 +1115,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprCast(ref e, _) |
hir::ExprType(ref e, _) |
hir::ExprUnary(_, ref e) |
hir::ExprYield(ref e) |
hir::ExprRepeat(ref e, _) => {
self.propagate_through_expr(&e, succ)
}
@ -1224,18 +1224,23 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
}
fn access_var(&mut self, id: NodeId, nid: NodeId, succ: LiveNode, acc: u32, span: Span)
-> LiveNode {
let ln = self.live_node(id, span);
if acc != 0 {
self.init_from_succ(ln, succ);
let var = self.variable(nid, span);
self.acc(ln, var, acc);
}
ln
}
fn access_path(&mut self, id: NodeId, path: &hir::Path, succ: LiveNode, acc: u32)
-> LiveNode {
match path.def {
Def::Local(def_id) => {
let nid = self.ir.tcx.hir.as_local_node_id(def_id).unwrap();
let ln = self.live_node(id, path.span);
if acc != 0 {
self.init_from_succ(ln, succ);
let var = self.variable(nid, path.span);
self.acc(ln, var, acc);
}
ln
self.access_var(id, nid, succ, acc, path.span)
}
_ => succ
}
@ -1398,7 +1403,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
hir::ExprBreak(..) | hir::ExprAgain(..) | hir::ExprLit(_) |
hir::ExprBlock(..) | hir::ExprAddrOf(..) |
hir::ExprStruct(..) | hir::ExprRepeat(..) |
hir::ExprClosure(..) | hir::ExprPath(_) |
hir::ExprClosure(..) | hir::ExprPath(_) | hir::ExprYield(..) |
hir::ExprBox(..) | hir::ExprType(..) => {
intravisit::walk_expr(this, expr);
}

View file

@ -625,7 +625,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
hir::ExprAddrOf(..) | hir::ExprCall(..) |
hir::ExprAssign(..) | hir::ExprAssignOp(..) |
hir::ExprClosure(..) | hir::ExprRet(..) |
hir::ExprUnary(..) |
hir::ExprUnary(..) | hir::ExprYield(..) |
hir::ExprMethodCall(..) | hir::ExprCast(..) |
hir::ExprArray(..) | hir::ExprTup(..) | hir::ExprIf(..) |
hir::ExprBinary(..) | hir::ExprWhile(..) |
@ -725,9 +725,14 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
// FnMut | copied -> &'env mut | upvar -> &'env mut -> &'up bk
// FnOnce | copied | upvar -> &'up bk
let kind = match self.tables.closure_kinds().get(fn_hir_id) {
Some(&(kind, _)) => kind,
None => span_bug!(span, "missing closure kind")
let kind = match self.node_ty(fn_hir_id)?.sty {
ty::TyGenerator(..) => ty::ClosureKind::FnOnce,
_ => {
match self.tables.closure_kinds().get(fn_hir_id) {
Some(&(kind, _)) => kind,
None => span_bug!(span, "missing closure kind"),
}
}
};
let closure_expr_def_index = self.tcx.hir.local_def_id(fn_node_id).index;

View file

@ -296,7 +296,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
hir::ImplItemKind::Type(_) => {}
}
}
hir_map::NodeExpr(&hir::Expr { node: hir::ExprClosure(.., body, _), .. }) => {
hir_map::NodeExpr(&hir::Expr { node: hir::ExprClosure(.., body, _, _), .. }) => {
self.visit_nested_body(body);
}
// Nothing to recurse on for these

View file

@ -24,6 +24,7 @@ use std::mem;
use std::rc::Rc;
use syntax::codemap;
use syntax::ast;
use syntax::ast::NodeId;
use syntax_pos::Span;
use ty::TyCtxt;
use ty::maps::Providers;
@ -32,6 +33,7 @@ use hir;
use hir::def_id::DefId;
use hir::intravisit::{self, Visitor, NestedVisitorMap};
use hir::{Block, Arm, Pat, PatKind, Stmt, Expr, Local};
use hir::map::Node;
use mir::transform::MirSource;
/// CodeExtent represents a statically-describable extent that can be
@ -789,7 +791,7 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
match expr.node {
// Manually recurse over closures, because they are the only
// case of nested bodies that share the parent environment.
hir::ExprClosure(.., body, _) => {
hir::ExprClosure(.., body, _, _) => {
let body = visitor.tcx.hir.body(body);
visitor.visit_body(body);
}
@ -1166,6 +1168,102 @@ fn region_maps<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
Rc::new(maps)
}
struct YieldFinder<'a> {
cache: &'a mut FxHashMap<NodeId, Option<Span>>,
result: Option<Span>,
}
impl<'a> YieldFinder<'a> {
fn lookup<F: FnOnce(&mut Self)>(&mut self, id: NodeId, f: F) {
// Don't traverse further if we found a yield expression
if self.result.is_some() {
return;
}
// See if there's an entry in the cache
if let Some(result) = self.cache.get(&id) {
self.result = *result;
return;
}
// Otherwise calculate the result and insert it into the cache
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,

View file

@ -21,7 +21,7 @@ use rustc_data_structures::control_flow_graph::ControlFlowGraph;
use hir::def::CtorKind;
use hir::def_id::DefId;
use ty::subst::{Subst, Substs};
use ty::{self, AdtDef, ClosureSubsts, Region, Ty};
use ty::{self, AdtDef, ClosureSubsts, Region, Ty, GeneratorInterior};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use util::ppaux;
use rustc_back::slice;
@ -104,6 +104,15 @@ pub struct Mir<'tcx> {
/// Return type of the function.
pub return_ty: Ty<'tcx>,
/// Yield type of the function, if it is a generator.
pub yield_ty: Option<Ty<'tcx>>,
/// Generator drop glue
pub generator_drop: Option<Box<Mir<'tcx>>>,
/// The layout of a generator. Produced by the state transformation.
pub generator_layout: Option<GeneratorLayout<'tcx>>,
/// Declarations of locals.
///
/// The first local is the return value pointer, followed by `arg_count`
@ -144,6 +153,7 @@ impl<'tcx> Mir<'tcx> {
visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
promoted: IndexVec<Promoted, Mir<'tcx>>,
return_ty: Ty<'tcx>,
yield_ty: Option<Ty<'tcx>>,
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
arg_count: usize,
upvar_decls: Vec<UpvarDecl>,
@ -159,6 +169,9 @@ impl<'tcx> Mir<'tcx> {
visibility_scopes,
promoted,
return_ty,
yield_ty,
generator_drop: None,
generator_layout: None,
local_decls,
arg_count,
upvar_decls,
@ -270,6 +283,9 @@ impl_stable_hash_for!(struct Mir<'tcx> {
visibility_scopes,
promoted,
return_ty,
yield_ty,
generator_drop,
generator_layout,
local_decls,
arg_count,
upvar_decls,
@ -395,6 +411,22 @@ pub struct LocalDecl<'tcx> {
/// True if this corresponds to a user-declared local variable.
pub is_user_variable: bool,
/// True if this is an internal local
///
/// These locals are not based on types in the source code and are only used
/// for drop flags at the moment.
///
/// The generator transformation will sanity check the locals which are live
/// across a suspension point against the type components of the generator
/// which type checking knows are live across a suspension point. We need to
/// flag drop flags to avoid triggering this check as they are introduced
/// after typeck.
///
/// This should be sound because the drop flags are fully algebraic, and
/// therefore don't affect the OIBIT or outlives properties of the
/// generator.
pub internal: bool,
/// Type of this local.
pub ty: Ty<'tcx>,
@ -420,6 +452,23 @@ impl<'tcx> LocalDecl<'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
}
}
@ -436,6 +485,7 @@ impl<'tcx> LocalDecl<'tcx> {
span,
scope: ARGUMENT_VISIBILITY_SCOPE
},
internal: false,
name: None, // FIXME maybe we do want some name here?
is_user_variable: false
}
@ -567,7 +617,20 @@ pub enum TerminatorKind<'tcx> {
msg: AssertMessage<'tcx>,
target: BasicBlock,
cleanup: Option<BasicBlock>
}
},
/// A suspend point
Yield {
/// The value to return
value: Operand<'tcx>,
/// Where to resume to
resume: BasicBlock,
/// Cleanup to be done if the generator is dropped at this suspend point
drop: Option<BasicBlock>,
},
/// Indicates the end of the dropping of a generator
GeneratorDrop,
}
impl<'tcx> Terminator<'tcx> {
@ -597,7 +660,7 @@ impl<'tcx> TerminatorKind<'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(),
@ -605,6 +668,8 @@ impl<'tcx> TerminatorKind<'tcx> {
slice::ref_slice(t).into_cow(),
Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
Yield { resume: t, drop: Some(c), .. } => vec![t, c].into_cow(),
Yield { resume: ref t, drop: None, .. } => slice::ref_slice(t).into_cow(),
DropAndReplace { target, unwind: Some(unwind), .. } |
Drop { target, unwind: Some(unwind), .. } => {
vec![target, unwind].into_cow()
@ -625,13 +690,15 @@ impl<'tcx> TerminatorKind<'tcx> {
match *self {
Goto { target: ref mut b } => vec![b],
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
Resume => Vec::new(),
Resume | GeneratorDrop => Vec::new(),
Return => Vec::new(),
Unreachable => Vec::new(),
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
Call { destination: None, cleanup: None, .. } => vec![],
Yield { resume: ref mut t, drop: Some(ref mut c), .. } => vec![t, c],
Yield { resume: ref mut t, drop: None, .. } => vec![t],
DropAndReplace { ref mut target, unwind: Some(ref mut unwind), .. } |
Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind],
DropAndReplace { ref mut target, unwind: None, .. } |
@ -664,6 +731,14 @@ impl<'tcx> BasicBlockData<'tcx> {
pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> {
self.terminator.as_mut().expect("invalid terminator state")
}
pub fn retain_statements<F>(&mut self, mut f: F) where F: FnMut(&mut Statement) -> bool {
for s in &mut self.statements {
if !f(s) {
s.kind = StatementKind::Nop;
}
}
}
}
impl<'tcx> Debug for TerminatorKind<'tcx> {
@ -703,7 +778,9 @@ impl<'tcx> 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, .. } =>
@ -737,6 +814,12 @@ impl<'tcx> TerminatorKind<'tcx> {
AssertMessage::Math(ref err) => {
write!(fmt, "{:?}", err.description())?;
}
AssertMessage::GeneratorResumedAfterReturn => {
write!(fmt, "{:?}", "generator resumed after completion")?;
}
AssertMessage::GeneratorResumedAfterPanic => {
write!(fmt, "{:?}", "generator resumed after panicking")?;
}
}
write!(fmt, ")")
@ -748,7 +831,7 @@ impl<'tcx> TerminatorKind<'tcx> {
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
use self::TerminatorKind::*;
match *self {
Return | Resume | Unreachable => vec![],
Return | Resume | Unreachable | GeneratorDrop => vec![],
Goto { .. } => vec!["".into()],
SwitchInt { ref values, .. } => {
values.iter()
@ -765,6 +848,9 @@ impl<'tcx> TerminatorKind<'tcx> {
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
Call { destination: None, cleanup: None, .. } => vec![],
Yield { drop: Some(_), .. } =>
vec!["resume".into_cow(), "drop".into_cow()],
Yield { drop: None, .. } => vec!["resume".into_cow()],
DropAndReplace { unwind: None, .. } |
Drop { unwind: None, .. } => vec!["return".into_cow()],
DropAndReplace { unwind: Some(_), .. } |
@ -784,7 +870,9 @@ pub enum AssertMessage<'tcx> {
len: Operand<'tcx>,
index: Operand<'tcx>
},
Math(ConstMathErr)
Math(ConstMathErr),
GeneratorResumedAfterReturn,
GeneratorResumedAfterPanic,
}
///////////////////////////////////////////////////////////////////////////
@ -1178,6 +1266,7 @@ pub enum AggregateKind<'tcx> {
/// number and is present only for union expressions.
Adt(&'tcx AdtDef, usize, &'tcx Substs<'tcx>, Option<usize>),
Closure(DefId, ClosureSubsts<'tcx>),
Generator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
@ -1339,6 +1428,31 @@ impl<'tcx> Debug for Rvalue<'tcx> {
write!(fmt, "[closure]")
}
}),
AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
let name = format!("[generator@{:?}]", tcx.hir.span(node_id));
let mut struct_fmt = fmt.debug_struct(&name);
tcx.with_freevars(node_id, |freevars| {
for (freevar, lv) in freevars.iter().zip(lvs) {
let def_id = freevar.def.def_id();
let var_id = tcx.hir.as_local_node_id(def_id).unwrap();
let var_name = tcx.local_var_name_str(var_id);
struct_fmt.field(&var_name, lv);
}
struct_fmt.field("$state", &lvs[freevars.len()]);
for i in (freevars.len() + 1)..lvs.len() {
struct_fmt.field(&format!("${}", i - freevars.len() - 1),
&lvs[i]);
}
});
struct_fmt.finish()
} else {
write!(fmt, "[generator]")
}
}),
}
}
}
@ -1483,6 +1597,11 @@ impl Location {
}
}
/// The layout of generator state
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct GeneratorLayout<'tcx> {
pub fields: Vec<LocalDecl<'tcx>>,
}
/*
* TypeFoldable implementations for MIR types
@ -1495,6 +1614,9 @@ impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> {
visibility_scopes: self.visibility_scopes.clone(),
promoted: self.promoted.fold_with(folder),
return_ty: self.return_ty.fold_with(folder),
yield_ty: self.yield_ty.fold_with(folder),
generator_drop: self.generator_drop.fold_with(folder),
generator_layout: self.generator_layout.fold_with(folder),
local_decls: self.local_decls.fold_with(folder),
arg_count: self.arg_count,
upvar_decls: self.upvar_decls.clone(),
@ -1506,12 +1628,27 @@ impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> {
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.basic_blocks.visit_with(visitor) ||
self.generator_drop.visit_with(visitor) ||
self.generator_layout.visit_with(visitor) ||
self.yield_ty.visit_with(visitor) ||
self.promoted.visit_with(visitor) ||
self.return_ty.visit_with(visitor) ||
self.local_decls.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for GeneratorLayout<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
GeneratorLayout {
fields: self.fields.fold_with(folder),
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.fields.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for LocalDecl<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
LocalDecl {
@ -1638,6 +1775,11 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
target,
unwind,
},
Yield { ref value, resume, drop } => Yield {
value: value.fold_with(folder),
resume: resume,
drop: drop,
},
Call { ref func, ref args, ref destination, cleanup } => {
let dest = destination.as_ref().map(|&(ref loc, dest)| {
(loc.fold_with(folder), dest)
@ -1667,6 +1809,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
cleanup,
}
},
GeneratorDrop => GeneratorDrop,
Resume => Resume,
Return => Return,
Unreachable => Unreachable,
@ -1686,6 +1829,8 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
Drop { ref location, ..} => location.visit_with(visitor),
DropAndReplace { ref location, ref value, ..} =>
location.visit_with(visitor) || value.visit_with(visitor),
Yield { ref value, ..} =>
value.visit_with(visitor),
Call { ref func, ref args, ref destination, .. } => {
let dest = if let Some((ref loc, _)) = *destination {
loc.visit_with(visitor)
@ -1706,6 +1851,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
Goto { .. } |
Resume |
Return |
GeneratorDrop |
Unreachable => false
}
}
@ -1751,7 +1897,11 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
AggregateKind::Adt(def, v, substs, n) =>
AggregateKind::Adt(def, v, substs.fold_with(folder), n),
AggregateKind::Closure(id, substs) =>
AggregateKind::Closure(id, substs.fold_with(folder))
AggregateKind::Closure(id, substs.fold_with(folder)),
AggregateKind::Generator(id, substs, interior) =>
AggregateKind::Generator(id,
substs.fold_with(folder),
interior.fold_with(folder)),
};
Aggregate(kind, fields.fold_with(folder))
}
@ -1777,7 +1927,9 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
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)
}
}

View file

@ -207,6 +207,9 @@ impl<'tcx> Rvalue<'tcx> {
AggregateKind::Closure(did, substs) => {
tcx.mk_closure_from_closure_substs(did, substs)
}
AggregateKind::Generator(did, substs, interior) => {
tcx.mk_generator(did, substs, interior)
}
}
}
}

View file

@ -33,7 +33,10 @@ pub enum MirSource {
Static(NodeId, hir::Mutability),
/// Promoted rvalues within a function.
Promoted(NodeId, Promoted)
Promoted(NodeId, Promoted),
/// Drop glue for a generator.
GeneratorDrop(NodeId),
}
impl<'a, 'tcx> MirSource {
@ -70,6 +73,7 @@ impl<'a, 'tcx> MirSource {
match *self {
MirSource::Fn(id) |
MirSource::Const(id) |
MirSource::GeneratorDrop(id) |
MirSource::Static(id, _) |
MirSource::Promoted(id, _) => id
}

View file

@ -11,7 +11,7 @@
use middle::const_val::ConstVal;
use hir::def_id::DefId;
use ty::subst::Substs;
use ty::{ClosureSubsts, Region, Ty};
use ty::{ClosureSubsts, Region, Ty, GeneratorInterior};
use mir::*;
use rustc_const_math::ConstUsize;
use syntax_pos::Span;
@ -226,6 +226,12 @@ macro_rules! make_mir_visitor {
self.super_closure_substs(substs);
}
fn visit_generator_interior(&mut self,
interior: & $($mutability)* GeneratorInterior<'tcx>,
_: Location) {
self.super_generator_interior(interior);
}
fn visit_const_val(&mut self,
const_val: & $($mutability)* ConstVal,
_: Location) {
@ -249,6 +255,10 @@ macro_rules! make_mir_visitor {
self.super_local_decl(local_decl);
}
fn visit_local(&mut self,
_local: & $($mutability)* Local) {
}
fn visit_visibility_scope(&mut self,
scope: & $($mutability)* VisibilityScope) {
self.super_visibility_scope(scope);
@ -415,6 +425,7 @@ macro_rules! make_mir_visitor {
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => {
}
@ -461,6 +472,15 @@ macro_rules! make_mir_visitor {
self.visit_branch(block, target);
cleanup.map(|t| self.visit_branch(block, t));
}
TerminatorKind::Yield { ref $($mutability)* value,
resume,
drop } => {
self.visit_operand(value, source_location);
self.visit_branch(block, resume);
drop.map(|t| self.visit_branch(block, t));
}
}
}
@ -475,7 +495,9 @@ macro_rules! make_mir_visitor {
self.visit_operand(len, location);
self.visit_operand(index, location);
}
AssertMessage::Math(_) => {}
AssertMessage::Math(_) => {},
AssertMessage::GeneratorResumedAfterReturn => {},
AssertMessage::GeneratorResumedAfterPanic => {},
}
}
@ -553,6 +575,13 @@ macro_rules! make_mir_visitor {
self.visit_def_id(def_id, location);
self.visit_closure_substs(closure_substs, location);
}
AggregateKind::Generator(ref $($mutability)* def_id,
ref $($mutability)* closure_substs,
ref $($mutability)* interior) => {
self.visit_def_id(def_id, location);
self.visit_closure_substs(closure_substs, location);
self.visit_generator_interior(interior, location);
}
}
for operand in operands {
@ -580,7 +609,8 @@ macro_rules! make_mir_visitor {
context: LvalueContext<'tcx>,
location: Location) {
match *lvalue {
Lvalue::Local(_) => {
Lvalue::Local(ref $($mutability)* local) => {
self.visit_local(local);
}
Lvalue::Static(ref $($mutability)* static_) => {
self.visit_static(static_, context, location);
@ -651,6 +681,7 @@ macro_rules! make_mir_visitor {
ref $($mutability)* ty,
name: _,
ref $($mutability)* source_info,
internal: _,
is_user_variable: _,
} = *local_decl;
@ -719,6 +750,10 @@ macro_rules! make_mir_visitor {
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>) {
}

View file

@ -301,7 +301,7 @@ fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool {
true
}
ty::TyClosure(..) | ty::TyAnon(..) => {
ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAnon(..) => {
bug!("ty_is_local invoked on unexpected type: {:?}", ty)
}
}

View file

@ -251,6 +251,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
AdtKind::Union => Some(16),
AdtKind::Enum => Some(17),
},
ty::TyGenerator(..) => Some(18),
ty::TyInfer(..) | ty::TyError => None
}
}

View file

@ -310,6 +310,9 @@ pub enum Vtable<'tcx, N> {
/// Same as above, but for a fn pointer type with the given signature.
VtableFnPointer(VtableFnPointerData<'tcx, N>),
/// Vtable automatically generated for a generator
VtableGenerator(VtableGeneratorData<'tcx, N>),
}
/// Identifies a particular impl in the source, along with a set of
@ -329,6 +332,15 @@ pub struct VtableImplData<'tcx, N> {
pub nested: Vec<N>
}
#[derive(Clone, PartialEq, Eq)]
pub struct VtableGeneratorData<'tcx, N> {
pub closure_def_id: DefId,
pub substs: ty::ClosureSubsts<'tcx>,
/// Nested obligations. This can be non-empty if the generator
/// signature contains associated types.
pub nested: Vec<N>
}
#[derive(Clone, PartialEq, Eq)]
pub struct VtableClosureData<'tcx, N> {
pub closure_def_id: DefId,
@ -743,6 +755,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
VtableBuiltin(i) => i.nested,
VtableDefaultImpl(d) => d.nested,
VtableClosure(c) => c.nested,
VtableGenerator(c) => c.nested,
VtableObject(d) => d.nested,
VtableFnPointer(d) => d.nested,
}
@ -754,6 +767,7 @@ impl<'tcx, N> Vtable<'tcx, N> {
&mut VtableParam(ref mut n) => n,
&mut VtableBuiltin(ref mut i) => &mut i.nested,
&mut VtableDefaultImpl(ref mut d) => &mut d.nested,
&mut VtableGenerator(ref mut c) => &mut c.nested,
&mut VtableClosure(ref mut c) => &mut c.nested,
&mut VtableObject(ref mut d) => &mut d.nested,
&mut VtableFnPointer(ref mut d) => &mut d.nested,
@ -784,6 +798,11 @@ impl<'tcx, N> Vtable<'tcx, N> {
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,

View file

@ -19,6 +19,7 @@ use super::PredicateObligation;
use super::SelectionContext;
use super::SelectionError;
use super::VtableClosureData;
use super::VtableGeneratorData;
use super::VtableFnPointerData;
use super::VtableImplData;
use super::util;
@ -882,6 +883,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>(
match vtable {
super::VtableClosure(_) |
super::VtableGenerator(_) |
super::VtableFnPointer(_) |
super::VtableObject(_) => {
debug!("assemble_candidates_from_impls: vtable={:?}",
@ -1041,6 +1043,8 @@ fn confirm_select_candidate<'cx, 'gcx, 'tcx>(
match vtable {
super::VtableImpl(data) =>
confirm_impl_candidate(selcx, obligation, data),
super::VtableGenerator(data) =>
confirm_generator_candidate(selcx, obligation, data),
super::VtableClosure(data) =>
confirm_closure_candidate(selcx, obligation, data),
super::VtableFnPointer(data) =>
@ -1123,6 +1127,60 @@ fn confirm_object_candidate<'cx, 'gcx, 'tcx>(
confirm_param_env_candidate(selcx, obligation, env_predicate)
}
fn confirm_generator_candidate<'cx, 'gcx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>)
-> Progress<'tcx>
{
let gen_sig = selcx.infcx().generator_sig(vtable.closure_def_id).unwrap()
.subst(selcx.tcx(), vtable.substs.substs);
let Normalized {
value: gen_sig,
obligations
} = normalize_with_depth(selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth+1,
&gen_sig);
debug!("confirm_generator_candidate: obligation={:?},gen_sig={:?},obligations={:?}",
obligation,
gen_sig,
obligations);
let tcx = selcx.tcx();
let gen_def_id = tcx.lang_items.gen_trait().unwrap();
// Note: we unwrap the binder here but re-create it below (1)
let ty::Binder((trait_ref, yield_ty, return_ty)) =
tcx.generator_trait_ref_and_outputs(gen_def_id,
obligation.predicate.self_ty(),
gen_sig);
let name = tcx.associated_item(obligation.predicate.item_def_id).name;
let ty = if name == Symbol::intern("Return") {
return_ty
} else if name == Symbol::intern("Yield") {
yield_ty
} else {
bug!()
};
let predicate = ty::Binder(ty::ProjectionPredicate { // (1) recreate binder here
projection_ty: ty::ProjectionTy {
substs: trait_ref.substs,
item_def_id: obligation.predicate.item_def_id,
},
ty: ty
});
confirm_param_env_candidate(selcx, obligation, predicate)
.with_addl_obligations(vtable.nested)
.with_addl_obligations(obligations)
}
fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,

View file

@ -24,9 +24,9 @@ use super::{ObjectCastObligation, Obligation};
use super::TraitNotObjectSafe;
use super::Selection;
use super::SelectionResult;
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableGenerator,
VtableFnPointer, VtableObject, VtableDefaultImpl};
use super::{VtableImplData, VtableObjectData, VtableBuiltinData,
use super::{VtableImplData, VtableObjectData, VtableBuiltinData, VtableGeneratorData,
VtableClosureData, VtableDefaultImplData, VtableFnPointerData};
use super::util;
@ -43,6 +43,7 @@ use middle::lang_items;
use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec};
use std::iter;
use std::cell::RefCell;
use std::cmp;
use std::fmt;
@ -197,6 +198,10 @@ enum SelectionCandidate<'tcx> {
/// confirmation step what ClosureKind obligation to emit.
ClosureCandidate(/* closure */ DefId, ty::ClosureSubsts<'tcx>, ty::ClosureKind),
/// Implementation of a `Generator` trait by one of the anonymous types
/// generated for a generator.
GeneratorCandidate(/* function / closure */ DefId, ty::ClosureSubsts<'tcx>),
/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int)->int`)
FnPointerCandidate,
@ -228,6 +233,11 @@ impl<'a, 'tcx> ty::Lift<'tcx> for SelectionCandidate<'a> {
ParamCandidate(ref trait_ref) => {
return tcx.lift(trait_ref).map(ParamCandidate);
}
GeneratorCandidate(def_id, ref substs) => {
return tcx.lift(substs).map(|substs| {
GeneratorCandidate(def_id, substs)
});
}
ClosureCandidate(def_id, ref substs, kind) => {
return tcx.lift(substs).map(|substs| {
ClosureCandidate(def_id, substs, kind)
@ -1296,14 +1306,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
} else if self.tcx().lang_items.unsize_trait() == Some(def_id) {
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else {
if self.tcx().lang_items.clone_trait() == Some(def_id) {
// Same builtin conditions as `Copy`, i.e. every type which has builtin support
// for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone`
// types have builtin support for `Clone`.
let clone_conditions = self.copy_conditions(obligation);
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?;
}
if self.tcx().lang_items.clone_trait() == Some(def_id) {
// Same builtin conditions as `Copy`, i.e. every type which has builtin support
// for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone`
// types have builtin support for `Clone`.
let clone_conditions = self.copy_conditions(obligation);
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?;
}
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)?;
@ -1488,6 +1499,38 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
})
}
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.
///
@ -1854,6 +1897,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
ImplCandidate(..) |
ClosureCandidate(..) |
GeneratorCandidate(..) |
FnPointerCandidate |
BuiltinObjectCandidate |
BuiltinUnsizeCandidate |
@ -1934,7 +1978,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) |
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) |
ty::TyChar | ty::TyRef(..) |
ty::TyChar | ty::TyRef(..) | ty::TyGenerator(..) |
ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever |
ty::TyError => {
// safe for everything
@ -1986,7 +2030,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
ty::TyClosure(..) |
ty::TyClosure(..) | ty::TyGenerator(..) |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
Never
}
@ -2087,6 +2131,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
substs.upvar_tys(def_id, self.tcx()).collect()
}
ty::TyGenerator(def_id, ref substs, interior) => {
let witness = iter::once(interior.witness);
substs.upvar_tys(def_id, self.tcx()).chain(witness).collect()
}
// for `PhantomData<T>`, we pass `T`
ty::TyAdt(def, substs) if def.is_phantom_data() => {
substs.types().collect()
@ -2196,6 +2245,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
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
@ -2528,6 +2583,40 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations })
}
fn confirm_generator_candidate(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: DefId,
substs: ty::ClosureSubsts<'tcx>)
-> Result<VtableGeneratorData<'tcx, PredicateObligation<'tcx>>,
SelectionError<'tcx>>
{
debug!("confirm_generator_candidate({:?},{:?},{:?})",
obligation,
closure_def_id,
substs);
let Normalized {
value: trait_ref,
obligations
} = self.generator_trait_ref(obligation, closure_def_id, substs);
debug!("confirm_generator_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})",
closure_def_id,
trait_ref,
obligations);
self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.param_env,
obligation.predicate.to_poly_trait_ref(),
trait_ref)?;
Ok(VtableGeneratorData {
closure_def_id: closure_def_id,
substs: substs.clone(),
nested: obligations
})
}
fn confirm_closure_candidate(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: DefId,
@ -3029,6 +3118,45 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
&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

View file

@ -53,6 +53,9 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::Vtable<'tcx, N> {
super::VtableClosure(ref d) =>
write!(f, "{:?}", d),
super::VtableGenerator(ref d) =>
write!(f, "{:?}", d),
super::VtableFnPointer(ref d) =>
write!(f, "VtableFnPointer({:?})", d),
@ -77,6 +80,15 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableImplData<'tcx, N> {
}
}
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableGeneratorData<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableGenerator(closure_def_id={:?}, substs={:?}, nested={:?})",
self.closure_def_id,
self.substs,
self.nested)
}
}
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::VtableClosureData<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "VtableClosure(closure_def_id={:?}, substs={:?}, nested={:?})",
@ -278,6 +290,19 @@ impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> {
})
}
traits::VtableDefaultImpl(t) => Some(traits::VtableDefaultImpl(t)),
traits::VtableGenerator(traits::VtableGeneratorData {
closure_def_id,
substs,
nested
}) => {
tcx.lift(&substs).map(|substs| {
traits::VtableGenerator(traits::VtableGeneratorData {
closure_def_id: closure_def_id,
substs: substs,
nested: nested
})
})
}
traits::VtableClosure(traits::VtableClosureData {
closure_def_id,
substs,
@ -351,6 +376,20 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableImplData<
}
}
impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableGeneratorData<'tcx, N> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
traits::VtableGeneratorData {
closure_def_id: self.closure_def_id,
substs: self.substs.fold_with(folder),
nested: self.nested.fold_with(folder),
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.substs.visit_with(visitor) || self.nested.visit_with(visitor)
}
}
impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::VtableClosureData<'tcx, N> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
traits::VtableClosureData {
@ -422,6 +461,9 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
match *self {
traits::VtableImpl(ref v) => traits::VtableImpl(v.fold_with(folder)),
traits::VtableDefaultImpl(ref t) => traits::VtableDefaultImpl(t.fold_with(folder)),
traits::VtableGenerator(ref d) => {
traits::VtableGenerator(d.fold_with(folder))
}
traits::VtableClosure(ref d) => {
traits::VtableClosure(d.fold_with(folder))
}
@ -438,6 +480,7 @@ impl<'tcx, N: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Vtable<'tcx, N>
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),

View file

@ -513,6 +513,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
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) => {

View file

@ -32,7 +32,7 @@ use ty::ReprOptions;
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;
@ -340,6 +340,10 @@ pub struct TypeckTables<'tcx> {
/// that caused the closure to be this kind.
closure_kinds: ItemLocalMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>,
generator_sigs: ItemLocalMap<Option<ty::GenSig<'tcx>>>,
generator_interiors: ItemLocalMap<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
@ -381,6 +385,8 @@ impl<'tcx> TypeckTables<'tcx> {
adjustments: ItemLocalMap(),
pat_binding_modes: ItemLocalMap(),
upvar_capture_map: FxHashMap(),
generator_sigs: ItemLocalMap(),
generator_interiors: ItemLocalMap(),
closure_tys: ItemLocalMap(),
closure_kinds: ItemLocalMap(),
liberated_fn_sigs: ItemLocalMap(),
@ -634,6 +640,42 @@ impl<'tcx> TypeckTables<'tcx> {
data: &mut self.cast_kinds
}
}
pub fn generator_sigs(&self)
-> LocalTableInContext<Option<ty::GenSig<'tcx>>>
{
LocalTableInContext {
local_id_root: self.local_id_root,
data: &self.generator_sigs,
}
}
pub fn generator_sigs_mut(&mut self)
-> LocalTableInContextMut<Option<ty::GenSig<'tcx>>>
{
LocalTableInContextMut {
local_id_root: self.local_id_root,
data: &mut self.generator_sigs,
}
}
pub fn generator_interiors(&self)
-> LocalTableInContext<ty::GeneratorInterior<'tcx>>
{
LocalTableInContext {
local_id_root: self.local_id_root,
data: &self.generator_interiors,
}
}
pub fn generator_interiors_mut(&mut self)
-> LocalTableInContextMut<ty::GeneratorInterior<'tcx>>
{
LocalTableInContextMut {
local_id_root: self.local_id_root,
data: &mut self.generator_interiors,
}
}
}
impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for TypeckTables<'gcx> {
@ -658,6 +700,8 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for Typeck
ref used_trait_imports,
tainted_by_errors,
ref free_region_map,
ref generator_sigs,
ref generator_interiors,
} = *self;
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
@ -691,6 +735,8 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for Typeck
ich::hash_stable_itemlocalmap(hcx, hasher, liberated_fn_sigs);
ich::hash_stable_itemlocalmap(hcx, hasher, fru_field_types);
ich::hash_stable_itemlocalmap(hcx, hasher, cast_kinds);
ich::hash_stable_itemlocalmap(hcx, hasher, generator_sigs);
ich::hash_stable_itemlocalmap(hcx, hasher, generator_interiors);
ich::hash_stable_hashset(hcx, hasher, used_trait_imports, |hcx, def_id| {
hcx.def_path_hash(*def_id)
@ -1364,7 +1410,7 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
pub fn print_debug_stats(self) {
sty_debug_print!(
self,
TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr,
TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyGenerator,
TyDynamic, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon);
println!("Substs interner: #{}", self.interners.substs.borrow().len());
@ -1719,6 +1765,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
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))
}

View file

@ -213,6 +213,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
|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(),

View file

@ -30,6 +30,7 @@ pub enum SimplifiedType {
TupleSimplifiedType(usize),
TraitSimplifiedType(DefId),
ClosureSimplifiedType(DefId),
GeneratorSimplifiedType(DefId),
AnonSimplifiedType(DefId),
FunctionSimplifiedType(usize),
ParameterSimplifiedType,
@ -72,6 +73,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
ty::TyClosure(def_id, _) => {
Some(ClosureSimplifiedType(def_id))
}
ty::TyGenerator(def_id, _, _) => {
Some(GeneratorSimplifiedType(def_id))
}
ty::TyNever => Some(NeverSimplifiedType),
ty::TyTuple(ref tys, _) => {
Some(TupleSimplifiedType(tys.len()))

View file

@ -85,6 +85,13 @@ impl FlagComputation {
}
}
&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);

View file

@ -345,6 +345,7 @@ pub fn characteristic_def_id_of_type(ty: Ty) -> Option<DefId> {
ty::TyFnDef(def_id, _) |
ty::TyClosure(def_id, _) => Some(def_id),
ty::TyGenerator(def_id, _, _) => Some(def_id),
ty::TyBool |
ty::TyChar |

View file

@ -1226,7 +1226,17 @@ impl<'a, 'tcx> Layout {
Univariant { variant: unit, non_zero: false }
}
// Tuples and closures.
// Tuples, generators and closures.
ty::TyGenerator(def_id, ref substs, _) => {
let tys = substs.field_tys(def_id, tcx);
let st = Struct::new(dl,
&tys.map(|ty| ty.layout(tcx, param_env))
.collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::AlwaysSizedUnivariant, ty)?;
Univariant { variant: st, non_zero: false }
}
ty::TyClosure(def_id, ref substs) => {
let tys = substs.upvar_tys(def_id, tcx);
let st = Struct::new(dl,
@ -2240,11 +2250,15 @@ impl<'a, 'tcx> TyLayout<'tcx> {
ty::TySlice(element) => element,
ty::TyStr => tcx.types.u8,
// Tuples and closures.
// Tuples, generators and closures.
ty::TyClosure(def_id, ref substs) => {
substs.upvar_tys(def_id, tcx).nth(i).unwrap()
}
ty::TyGenerator(def_id, ref substs, _) => {
substs.field_tys(def_id, tcx).nth(i).unwrap()
}
ty::TyTuple(tys, _) => tys[i],
// SIMD vector types.

View file

@ -597,8 +597,15 @@ macro_rules! define_maps {
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
}
}
}
@ -1006,6 +1013,10 @@ define_maps! { <'tcx>
/// 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: GenSignature(DefId) -> Option<ty::PolyGenSig<'tcx>>,
/// Caches CoerceUnsized kinds for impls on custom types.
[] coerce_unsized_info: CoerceUnsizedInfo(DefId)
-> ty::adjustment::CoerceUnsizedInfo,

View file

@ -25,6 +25,7 @@ use middle::privacy::AccessLevels;
use middle::resolve_lifetime::ObjectLifetimeDefault;
use middle::region::CodeExtent;
use mir::Mir;
use mir::GeneratorLayout;
use traits;
use ty;
use ty::subst::{Subst, Substs};
@ -59,9 +60,9 @@ use rustc_data_structures::transitive_relation::TransitiveRelation;
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};
@ -409,6 +410,8 @@ bitflags! {
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
@ -1706,7 +1709,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
let result = match ty.sty {
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
TyRawPtr(..) | TyRef(..) | TyFnDef(..) | TyFnPtr(_) |
TyArray(..) | TyClosure(..) | TyNever => {
TyArray(..) | TyClosure(..) | TyGenerator(..) | TyNever => {
vec![]
}
@ -2039,6 +2042,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
hir::ExprBox(..) |
hir::ExprAddrOf(..) |
hir::ExprBinary(..) |
hir::ExprYield(..) |
hir::ExprCast(..) => {
false
}
@ -2271,6 +2275,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
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> {

View file

@ -115,6 +115,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
}
ty::TyGenerator(def_id, ref substs, ref interior) => {
// Same as the closure case
for upvar_ty in substs.upvar_tys(def_id, *self) {
self.compute_components(upvar_ty, out);
}
// But generators can have additional interior types
self.compute_components(interior.witness, out);
}
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
// is implied by the environment is done in regionck.
ty::TyParam(p) => {

View file

@ -389,6 +389,18 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound))
}
(&ty::TyGenerator(a_id, a_substs, a_interior),
&ty::TyGenerator(b_id, b_substs, b_interior))
if a_id == b_id =>
{
// All TyGenerator types with the same id represent
// the (anonymous) type of the same generator expression. So
// all of their regions should be equated.
let substs = relation.relate(&a_substs, &b_substs)?;
let interior = relation.relate(&a_interior, &b_interior)?;
Ok(tcx.mk_generator(a_id, substs, interior))
}
(&ty::TyClosure(a_id, a_substs),
&ty::TyClosure(b_id, b_substs))
if a_id == b_id =>
@ -512,6 +524,18 @@ impl<'tcx> Relate<'tcx> for ty::ClosureSubsts<'tcx> {
}
}
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>,

View file

@ -29,6 +29,15 @@ impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) {
}
}
impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) {
type Lifted = (A::Lifted, B::Lifted, C::Lifted);
fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
tcx.lift(&self.0).and_then(|a| {
tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))
})
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
type Lifted = Option<T::Lifted>;
fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@ -220,6 +229,15 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> {
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorInterior<'a> {
type Lifted = ty::GeneratorInterior<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
tcx.lift(&self.witness).map(|witness| {
ty::GeneratorInterior { witness }
})
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> {
type Lifted = ty::adjustment::Adjustment<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@ -283,6 +301,19 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> {
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> {
type Lifted = ty::GenSig<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
tcx.lift(&(self.yield_ty, self.return_ty))
.map(|(yield_ty, return_ty)| {
ty::GenSig {
yield_ty,
return_ty,
}
})
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> {
type Lifted = ty::FnSig<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@ -539,6 +570,9 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
ty::TyRef(ref r, tm) => {
ty::TyRef(r.fold_with(folder), tm.fold_with(folder))
}
ty::TyGenerator(did, substs, interior) => {
ty::TyGenerator(did, substs.fold_with(folder), interior.fold_with(folder))
}
ty::TyClosure(did, substs) => ty::TyClosure(did, substs.fold_with(folder)),
ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)),
ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)),
@ -570,6 +604,9 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
ty::TyFnDef(_, substs) => substs.visit_with(visitor),
ty::TyFnPtr(ref f) => f.visit_with(visitor),
ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor),
ty::TyGenerator(_did, ref substs, ref interior) => {
substs.visit_with(visitor) || interior.visit_with(visitor)
}
ty::TyClosure(_did, ref substs) => substs.visit_with(visitor),
ty::TyProjection(ref data) => data.visit_with(visitor),
ty::TyAnon(_, ref substs) => substs.visit_with(visitor),
@ -594,6 +631,20 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeAndMut<'tcx> {
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::GenSig<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::GenSig {
yield_ty: self.yield_ty.fold_with(folder),
return_ty: self.return_ty.fold_with(folder),
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.yield_ty.visit_with(visitor) ||
self.return_ty.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::FnSig<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
let inputs_and_output = self.inputs_and_output.fold_with(folder);
@ -684,6 +735,16 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ClosureSubsts<'tcx> {
}
}
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 {

View file

@ -135,6 +135,10 @@ pub enum TypeVariants<'tcx> {
/// `|a| a`.
TyClosure(DefId, ClosureSubsts<'tcx>),
/// The anonymous type of a generator. Used to represent the type of
/// `|a| yield a`.
TyGenerator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>),
/// The never type `!`
TyNever,
@ -261,6 +265,51 @@ impl<'a, 'gcx, 'acx, 'tcx> ClosureSubsts<'tcx> {
}
}
impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> {
/// This returns the types of the MIR locals which had to be stored across suspension points.
/// It is calculated in rustc_mir::transform::generator::StateTransform.
/// All the types here must be in the tuple in GeneratorInterior.
pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
{
let state = tcx.generator_layout(def_id).fields.iter();
state.map(move |d| d.ty.subst(tcx, self.substs))
}
/// This is the types of all the fields stored in a generator.
/// It includes the upvars, state types and the state discriminant which is u32.
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'a
{
let upvars = self.upvar_tys(def_id, tcx);
let state = self.state_tys(def_id, tcx);
upvars.chain(iter::once(tcx.types.u32)).chain(state)
}
}
/// This describes the types that can be contained in a generator.
/// It will be a type variable initially and unified in the last stages of typeck of a body.
/// It contains a tuple of all the types that could end up on a generator frame.
/// The state transformation MIR pass may only produce layouts which mention types in this tuple.
/// Upvars are not counted here.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct GeneratorInterior<'tcx> {
pub witness: Ty<'tcx>,
}
impl<'tcx> GeneratorInterior<'tcx> {
pub fn new(witness: Ty<'tcx>) -> GeneratorInterior<'tcx> {
GeneratorInterior { witness }
}
pub fn as_slice(&self) -> &'tcx Slice<Ty<'tcx>> {
match self.witness.sty {
ty::TyTuple(s, _) => s,
_ => bug!(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub enum ExistentialPredicate<'tcx> {
/// e.g. Iterator
@ -579,6 +628,22 @@ impl<'a, 'tcx> ProjectionTy<'tcx> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct GenSig<'tcx> {
pub yield_ty: Ty<'tcx>,
pub return_ty: Ty<'tcx>,
}
pub type PolyGenSig<'tcx> = Binder<GenSig<'tcx>>;
impl<'tcx> PolyGenSig<'tcx> {
pub fn yield_ty(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|sig| sig.yield_ty)
}
pub fn return_ty(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|sig| sig.return_ty)
}
}
/// Signature of a function type, which I have arbitrarily
/// decided to use to refer to the input/output types.
@ -1379,7 +1444,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
TyAdt(_, substs) | TyAnon(_, substs) => {
substs.regions().collect()
}
TyClosure(_, ref substs) => {
TyClosure(_, ref substs) | TyGenerator(_, ref substs, _) => {
substs.substs.regions().collect()
}
TyProjection(ref data) => {

View file

@ -27,6 +27,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
HashStable};
use rustc_data_structures::fx::FxHashMap;
use std::cmp;
use std::iter;
use std::hash::Hash;
use std::intrinsics;
use syntax::ast::{self, Name};
@ -573,6 +574,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}).collect()
}
ty::TyGenerator(def_id, substs, interior) => {
substs.upvar_tys(def_id, self).chain(iter::once(interior.witness)).map(|ty| {
self.dtorck_constraint_for_ty(span, for_ty, depth+1, ty)
}).collect()
}
ty::TyAdt(def, substs) => {
let ty::DtorckConstraint {
dtorck_types, outlives
@ -694,6 +701,7 @@ impl<'a, 'gcx, 'tcx, W> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx, W>
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),
@ -1120,6 +1128,11 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty::TyClosure(def_id, ref substs) => substs.upvar_tys(def_id, tcx).any(needs_drop),
// Pessimistically assume that all generators will require destructors
// as we don't know if a destructor is a noop or not until after the MIR
// state transformation pass
ty::TyGenerator(..) => true,
ty::TyTuple(ref tys, _) => tys.iter().cloned().any(needs_drop),
// unions don't have destructors regardless of the child types

View file

@ -112,6 +112,10 @@ fn push_subtypes<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent_ty: Ty<'tcx>) {
ty::TyClosure(_, ref substs) => {
stack.extend(substs.substs.types().rev());
}
ty::TyGenerator(_, ref substs, ref interior) => {
stack.extend(substs.substs.types().rev());
stack.push(interior.witness);
}
ty::TyTuple(ts, _) => {
stack.extend(ts.iter().cloned().rev());
}

View file

@ -281,8 +281,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
}
}
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.
//

View file

@ -17,7 +17,7 @@ use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
use ty::{TyBool, TyChar, TyAdt};
use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple};
use ty::{TyClosure, TyProjection, TyAnon};
use ty::{TyClosure, TyGenerator, TyProjection, TyAnon};
use ty::{TyDynamic, TyInt, TyUint, TyInfer};
use ty::{self, Ty, TyCtxt, TypeFoldable};
@ -715,6 +715,12 @@ impl<'tcx> fmt::Display for ty::TraitRef<'tcx> {
}
}
impl<'tcx> fmt::Display for ty::GeneratorInterior<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.witness.fmt(f)
}
}
impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@ -813,6 +819,41 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
})
}
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")?;

View file

@ -48,9 +48,8 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
move_error_collector: move_error::MoveErrorCollector::new(),
};
let body = glcx.bccx.tcx.hir.body(body);
euv::ExprUseVisitor::new(&mut glcx, bccx.tcx, param_env, &bccx.region_maps, bccx.tables)
.consume_body(body);
.consume_body(bccx.body);
glcx.report_potential_errors();
let GatherLoanCtxt { all_loans, move_data, .. } = glcx;
@ -127,7 +126,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
bk={:?}, loan_cause={:?})",
borrow_id, cmt, loan_region,
bk, loan_cause);
self.guarantee_valid(borrow_id,
borrow_span,
cmt,

View file

@ -36,7 +36,7 @@ use rustc::middle::region::{self, RegionMaps};
use rustc::middle::free_region::RegionRelations;
use rustc::ty::{self, TyCtxt};
use rustc::ty::maps::Providers;
use rustc::util::nodemap::FxHashMap;
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
use std::fmt;
@ -100,9 +100,8 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
let body_id = tcx.hir.body_owned_by(owner_id);
let tables = tcx.typeck_tables_of(owner_def_id);
let region_maps = tcx.region_maps(owner_def_id);
let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
let body = bccx.tcx.hir.body(body_id);
let body = tcx.hir.body(body_id);
let bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
// Eventually, borrowck will always read the MIR, but at the
// moment we do not. So, for now, we always force MIR to be
@ -139,10 +138,9 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
{
// Check the body of fn items.
let tcx = this.tcx;
let body = tcx.hir.body(body_id);
let id_range = {
let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.hir);
visitor.visit_body(body);
visitor.visit_body(this.body);
visitor.result()
};
let (all_loans, move_data) =
@ -163,7 +161,7 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
Some(body),
Some(this.body),
cfg,
LoanDataFlowOperator,
id_range,
@ -174,13 +172,13 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
loan.kill_scope.node_id(), loan_idx);
}
loan_dfcx.add_kills_from_flow_exits(cfg);
loan_dfcx.propagate(cfg, body);
loan_dfcx.propagate(cfg, this.body);
let flowed_moves = move_data::FlowedMoveData::new(move_data,
this,
cfg,
id_range,
body);
this.body);
Some(AnalysisData { all_loans,
loans: loan_dfcx,
@ -199,7 +197,8 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
let owner_def_id = tcx.hir.local_def_id(owner_id);
let tables = tcx.typeck_tables_of(owner_def_id);
let region_maps = tcx.region_maps(owner_def_id);
let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
let body = tcx.hir.body(body_id);
let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id, body };
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
(bccx, dataflow_data.unwrap())
@ -218,6 +217,8 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
region_maps: Rc<RegionMaps>,
owner_def_id: DefId,
body: &'tcx hir::Body,
}
impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> {
@ -349,7 +350,7 @@ fn closure_to_block(closure_id: DefIndex,
let closure_id = tcx.hir.def_index_to_node_id(closure_id);
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
}
_ => {
@ -549,9 +550,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
_ => { }
}
let mut db = self.bckerr_to_diag(&err);
self.note_and_explain_bckerr(&mut db, err);
db.emit();
self.report_bckerr(&err);
}
pub fn report_use_of_moved_value(&self,
@ -652,7 +651,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
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)"),
@ -722,8 +721,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
self.tcx.sess.struct_span_err_with_code(s, msg, code)
}
fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
let span = err.span.clone();
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, s: S, msg: &str, code: &str) {
self.tcx.sess.span_err_with_code(s, msg, code);
}
fn report_bckerr(&self, err: &BckError<'tcx>) {
let error_span = err.span.clone();
match err.code {
err_mutbl => {
@ -747,12 +750,16 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
}
};
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) |
@ -762,30 +769,228 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
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();
}
}
}
@ -1048,133 +1253,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
}
}
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 {

View file

@ -652,6 +652,92 @@ x.x = Some(&y);
```
"##,
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 = &String::new(); // <-- 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! {

View file

@ -9,9 +9,11 @@
// except according to those terms.
use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Deref, DerefMut, Range};
use std::slice;
use bitslice::{BitSlice, Word};
use bitslice::{bitwise, Union, Subtract};
use indexed_vec::Idx;
@ -21,6 +23,7 @@ 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>,
@ -109,6 +112,13 @@ impl<T: Idx> IdxSet<T> {
}
}
/// Removes all elements
pub fn clear(&mut self) {
for b in &mut self.bits {
*b = 0;
}
}
/// Removes `elem` from the set `self`; returns true iff this changed `self`.
pub fn remove(&mut self, elem: &T) -> bool {
self.bits.clear_bit(elem.index())
@ -154,6 +164,14 @@ impl<T: Idx> IdxSet<T> {
bitwise(self.words_mut(), other.words(), &Subtract)
}
pub fn iter(&self) -> Iter<T> {
Iter {
cur: None,
iter: self.words().iter().enumerate(),
_pd: PhantomData,
}
}
/// Calls `f` on each index value held in this set, up to the
/// bound `max_bits` on the size of universe of indexes.
pub fn each_bit<F>(&self, max_bits: usize, f: F) where F: FnMut(T) {
@ -218,3 +236,32 @@ fn each_bit<T: Idx, F>(words: &IdxSet<T>, max_bits: usize, mut f: F) where F: Fn
}
}
}
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,
}
}
}
}

View file

@ -38,7 +38,7 @@ impl Idx for u32 {
fn index(self) -> usize { self as usize }
}
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub struct IndexVec<I: Idx, T> {
pub raw: Vec<T>,
_marker: PhantomData<Fn(&I)>

View file

@ -292,6 +292,15 @@ impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> {
}
}
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,

View file

@ -1015,6 +1015,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
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"));

View file

@ -607,6 +607,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::TyInfer(..) |
ty::TyError |
ty::TyClosure(..) |
ty::TyGenerator(..) |
ty::TyProjection(..) |
ty::TyAnon(..) |
ty::TyFnDef(..) => bug!("Unexpected type in foreign function"),

View file

@ -105,6 +105,7 @@ provide! { <'tcx> tcx, def_id, cdata,
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) }

View file

@ -438,6 +438,7 @@ impl<'tcx> EntryKind<'tcx> {
EntryKind::Impl(_) |
EntryKind::DefaultImpl(_) |
EntryKind::Field |
EntryKind::Generator(_) |
EntryKind::Closure(_) => return None,
})
}
@ -1100,6 +1101,23 @@ impl<'a, 'tcx> CrateMetadata {
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)

View file

@ -1213,13 +1213,23 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
debug!("IsolatedEncoder::encode_info_for_closure({:?})", def_id);
let tcx = self.tcx;
let data = ClosureData {
kind: tcx.closure_kind(def_id),
sig: self.lazy(&tcx.fn_sig(def_id)),
let kind = if let Some(sig) = self.tcx.generator_sig(def_id) {
let layout = self.tcx.generator_layout(def_id);
let data = GeneratorData {
sig,
layout: layout.clone(),
};
EntryKind::Generator(self.lazy(&data))
} else {
let data = ClosureData {
kind: tcx.closure_kind(def_id),
sig: self.lazy(&tcx.fn_sig(def_id)),
};
EntryKind::Closure(self.lazy(&data))
};
Entry {
kind: EntryKind::Closure(self.lazy(&data)),
kind,
visibility: self.lazy(&ty::Visibility::Public),
span: self.lazy(&tcx.def_span(def_id)),
attributes: self.encode_attributes(&tcx.get_attrs(def_id)),

View file

@ -353,6 +353,7 @@ pub enum EntryKind<'tcx> {
Mod(Lazy<ModData>),
MacroDef(Lazy<MacroDef>),
Closure(Lazy<ClosureData<'tcx>>),
Generator(Lazy<GeneratorData<'tcx>>),
Trait(Lazy<TraitData<'tcx>>),
Impl(Lazy<ImplData<'tcx>>),
DefaultImpl(Lazy<ImplData<'tcx>>),
@ -401,6 +402,9 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for EntryK
EntryKind::MacroDef(ref macro_def) => {
macro_def.hash_stable(hcx, hasher);
}
EntryKind::Generator(data) => {
data.hash_stable(hcx, hasher);
}
EntryKind::Closure(closure_data) => {
closure_data.hash_stable(hcx, hasher);
}
@ -564,3 +568,10 @@ pub struct ClosureData<'tcx> {
pub sig: Lazy<ty::PolyFnSig<'tcx>>,
}
impl_stable_hash_for!(struct ClosureData<'tcx> { kind, sig });
#[derive(RustcEncodable, RustcDecodable)]
pub struct GeneratorData<'tcx> {
pub sig: ty::PolyGenSig<'tcx>,
pub layout: mir::GeneratorLayout<'tcx>,
}
impl_stable_hash_for!(struct GeneratorData<'tcx> { sig, layout });

View file

@ -279,12 +279,20 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
(index, span), flow_state);
}
AssertMessage::Math(_/*const_math_err*/) => {}
AssertMessage::GeneratorResumedAfterReturn => {}
AssertMessage::GeneratorResumedAfterPanic => {}
}
}
TerminatorKind::Yield { ref value, resume: _, drop: _} => {
self.consume_operand(ContextKind::Yield.new(loc),
Consume, (value, span), flow_state);
}
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => {
// no data used, thus irrelevant to borrowck
}
@ -1102,6 +1110,7 @@ enum ContextKind {
CallDest,
Assert,
StorageDead,
Yield,
}
impl ContextKind {

View file

@ -118,6 +118,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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) {

View file

@ -185,12 +185,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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 since it follows the upvars in the generator
// struct. See librustc_mir/transform/generator.rs for more details.
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
@ -232,6 +246,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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 { .. } |

View file

@ -77,6 +77,7 @@ impl Category {
ExprKind::Borrow { .. } |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::Yield { .. } |
ExprKind::InlineAsm { .. } =>
Some(Category::Rvalue(RvalueFunc::AsRvalue)),

View file

@ -284,6 +284,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ExprKind::Index { .. } |
ExprKind::Deref { .. } |
ExprKind::Literal { .. } |
ExprKind::Yield { .. } |
ExprKind::Field { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
Category::Rvalue(RvalueFunc::Into) => false,

View file

@ -712,6 +712,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ty: var_ty.clone(),
name: Some(name),
source_info,
internal: false,
is_user_variable: true,
});
self.var_indices.insert(var_id, var);

View file

@ -71,7 +71,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
// Assume that everything other than closures
// is a constant "initializer" expression.
match expr.node {
hir::ExprClosure(_, _, body, _) => body,
hir::ExprClosure(_, _, body, _, _) => body,
_ => hir::BodyId { node_id: expr.id }
}
}
@ -95,13 +95,18 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
let ty = tcx.type_of(tcx.hir.local_def_id(id));
let mut abi = fn_sig.abi;
let implicit_argument = if let ty::TyClosure(..) = ty.sty {
// HACK(eddyb) Avoid having RustCall on closures,
// as it adds unnecessary (and wrong) auto-tupling.
abi = Abi::Rust;
Some((closure_self_ty(tcx, id, body_id), None))
} else {
None
let implicit_argument = match ty.sty {
ty::TyClosure(..) => {
// HACK(eddyb) Avoid having RustCall on closures,
// as it adds unnecessary (and wrong) auto-tupling.
abi = Abi::Rust;
Some((closure_self_ty(tcx, id, body_id), None))
}
ty::TyGenerator(..) => {
let gen_ty = tcx.body_tables(body_id).node_id_to_type(fn_hir_id);
Some((gen_ty, None))
}
_ => None,
};
let body = tcx.hir.body(body_id);
@ -114,7 +119,15 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
});
let arguments = implicit_argument.into_iter().chain(explicit_arguments);
build::construct_fn(cx, id, arguments, abi, fn_sig.output(), body)
let (yield_ty, return_ty) = if body.is_generator {
let gen_sig = cx.tables().generator_sigs()[fn_hir_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)
};
@ -199,7 +212,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
///////////////////////////////////////////////////////////////////////////
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)
-> Ty<'tcx> {
@ -328,6 +341,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
arguments: A,
abi: Abi,
return_ty: Ty<'gcx>,
yield_ty: Option<Ty<'gcx>>,
body: &'gcx hir::Body)
-> Mir<'tcx>
where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
@ -336,7 +350,10 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
let tcx = hir.tcx();
let span = tcx.hir.span(fn_id);
let mut builder = Builder::new(hir.clone(), span, arguments.len(), return_ty);
let mut builder = Builder::new(hir.clone(),
span,
arguments.len(),
return_ty);
let call_site_extent = CodeExtent::CallSiteScope(body.id());
let arg_extent = CodeExtent::ParameterScope(body.id());
@ -391,7 +408,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
}).collect()
});
let mut mir = builder.finish(upvar_decls, return_ty);
let mut mir = builder.finish(upvar_decls, return_ty, yield_ty);
mir.spread_arg = spread_arg;
mir
}
@ -416,7 +433,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
// Constants can't `return` so a return block should not be created.
assert_eq!(builder.cached_return_block, None);
builder.finish(vec![], ty)
builder.finish(vec![], ty, None)
}
fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
@ -427,7 +444,7 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
let mut builder = Builder::new(hir, span, 0, ty);
let source_info = builder.source_info(span);
builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
builder.finish(vec![], ty)
builder.finish(vec![], ty, None)
}
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@ -462,7 +479,8 @@ 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() {
@ -474,6 +492,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.visibility_scopes,
IndexVec::new(),
return_ty,
yield_ty,
self.local_decls,
self.arg_count,
upvar_decls,
@ -506,6 +525,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
span: pattern.map_or(self.fn_span, |pat| pat.span)
},
name,
internal: false,
is_user_variable: false,
});
}

View file

@ -126,6 +126,9 @@ pub struct Scope<'tcx> {
/// The cache for drop chain on “normal” exit into a particular BasicBlock.
cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
/// The cache for drop chain on "generator drop" exit.
cached_generator_drop: Option<BasicBlock>,
}
#[derive(Debug)]
@ -140,14 +143,22 @@ struct DropData<'tcx> {
kind: DropKind
}
#[derive(Debug, Default, Clone, Copy)]
struct CachedBlock {
/// The cached block for the cleanups-on-diverge path. This block
/// contains code to run the current drop and all the preceding
/// drops (i.e. those having lower index in Drops 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 Drops Scope drop
/// array)
cached_block: Option<BasicBlock>
cached_block: CachedBlock,
},
Storage
}
@ -166,6 +177,29 @@ pub struct BreakableScope<'tcx> {
pub break_destination: Lvalue<'tcx>,
}
impl CachedBlock {
fn invalidate(&mut self) {
self.generator_drop = None;
self.unwind = None;
}
fn get(&self, generator_drop: bool) -> Option<BasicBlock> {
if generator_drop {
self.generator_drop
} else {
self.unwind
}
}
fn ref_mut(&mut self, generator_drop: bool) -> &mut Option<BasicBlock> {
if generator_drop {
&mut self.generator_drop
} else {
&mut self.unwind
}
}
}
impl DropKind {
fn may_panic(&self) -> bool {
match *self {
@ -187,7 +221,7 @@ impl<'tcx> Scope<'tcx> {
if !unwind { return; }
for dropdata in &mut self.drops {
if let DropKind::Value { ref mut cached_block } = dropdata.kind {
*cached_block = None;
cached_block.invalidate();
}
}
}
@ -196,10 +230,12 @@ impl<'tcx> Scope<'tcx> {
///
/// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for
/// this method to work correctly.
fn cached_block(&self) -> Option<BasicBlock> {
fn cached_block(&self, generator_drop: bool) -> Option<BasicBlock> {
let mut drops = self.drops.iter().rev().filter_map(|data| {
match data.kind {
DropKind::Value { cached_block } => Some(cached_block),
DropKind::Value { cached_block } => {
Some(cached_block.get(generator_drop))
}
DropKind::Storage => None
}
});
@ -294,6 +330,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
extent_span: extent.1.span,
needs_cleanup: false,
drops: vec![],
cached_generator_drop: None,
cached_exits: FxHashMap()
});
}
@ -319,7 +356,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
&scope,
&self.scopes,
block,
self.arg_count));
self.arg_count,
false));
self.cfg.push_end_region(block, extent.1, scope.extent);
block.unit()
@ -370,7 +408,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
scope,
rest,
block,
self.arg_count));
self.arg_count,
false));
// End all regions for scopes out of which we are breaking.
self.cfg.push_end_region(block, extent.1, scope.extent);
@ -381,6 +420,55 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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 theres 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 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);
}
self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop);
Some(result)
}
/// Creates a new visibility scope, nested in the current one.
pub fn new_visibility_scope(&mut self, span: Span) -> VisibilityScope {
let parent = self.visibility_scope;
@ -465,7 +553,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
None,
MirSource::Fn(_) =>
Some(self.topmost_scope()),
MirSource::Promoted(..) =>
MirSource::Promoted(..) |
MirSource::GeneratorDrop(..) =>
bug!(),
}
}
@ -481,7 +570,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
lvalue_ty: Ty<'tcx>) {
let needs_drop = self.hir.needs_drop(lvalue_ty);
let drop_kind = if needs_drop {
DropKind::Value { cached_block: None }
DropKind::Value { cached_block: CachedBlock::default() }
} else {
// Only temps and vars need their storage dead.
match *lvalue {
@ -567,6 +656,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// See module comment for more details. None indicates theres 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;
}
@ -599,7 +692,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
};
for scope in scopes.iter_mut() {
target = build_diverge_scope(cfg, scope.extent_span, scope, target);
target = build_diverge_scope(cfg, scope.extent_span, scope, target, generator_drop);
}
Some(target)
}
@ -676,7 +769,8 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
scope: &Scope<'tcx>,
earlier_scopes: &[Scope<'tcx>],
mut block: BasicBlock,
arg_count: usize)
arg_count: usize,
generator_drop: bool)
-> BlockAnd<()> {
debug!("build_scope_drops({:?} -> {:?})", block, scope);
let mut iter = scope.drops.iter().rev().peekable();
@ -688,16 +782,20 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
// for us to diverge into in case the drop panics.
let on_diverge = iter.peek().iter().filter_map(|dd| {
match dd.kind {
DropKind::Value { cached_block: None } =>
span_bug!(drop_data.span, "cached block not present?"),
DropKind::Value { cached_block } => cached_block,
DropKind::Value { cached_block } => {
let result = cached_block.get(generator_drop);
if result.is_none() {
span_bug!(drop_data.span, "cached block not present?")
}
result
},
DropKind::Storage => None
}
}).next();
// If theres 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 {
@ -710,6 +808,11 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
DropKind::Storage => {}
}
// We do not need to emit StorageDead for generator drops
if generator_drop {
continue
}
// Drop the storage for both value and storage drops.
// Only temps and vars need their storage dead.
match drop_data.location {
@ -728,7 +831,8 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
fn build_diverge_scope<'a, 'gcx, 'tcx>(cfg: &mut CFG<'tcx>,
span: Span,
scope: &mut Scope<'tcx>,
mut target: BasicBlock)
mut target: BasicBlock,
generator_drop: bool)
-> BasicBlock
{
// Build up the drops in **reverse** order. The end result will
@ -742,7 +846,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(cfg: &mut CFG<'tcx>,
// The code in this function reads from right to left. At each
// point, we check for cached blocks representing the
// remainder. If everything is cached, we'll just walk right to
// left reading the cached results but never created anything.
// left reading the cached results but never create anything.
let visibility_scope = scope.visibility_scope;
let source_info = |span| SourceInfo {
@ -764,7 +868,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(cfg: &mut CFG<'tcx>,
// match the behavior of clang, but on inspection eddyb says
// this is not what clang does.
let cached_block = match drop_data.kind {
DropKind::Value { ref mut cached_block } => cached_block,
DropKind::Value { ref mut cached_block } => cached_block.ref_mut(generator_drop),
DropKind::Storage => continue
};
target = if let Some(cached_block) = *cached_block {

View file

@ -653,15 +653,21 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
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 {

View file

@ -263,6 +263,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
match term.kind {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
@ -274,6 +275,10 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
// branching terminators - these don't move anything
}
TerminatorKind::Yield { ref value, .. } => {
self.gather_operand(loc, value);
}
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
self.gather_move(loc, location);
}

View file

@ -432,8 +432,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
hir::ExprClosure(..) => {
let closure_ty = cx.tables().expr_ty(expr);
let (def_id, substs) = match closure_ty.sty {
ty::TyClosure(def_id, substs) => (def_id, substs),
let (def_id, substs, interior) = match closure_ty.sty {
ty::TyClosure(def_id, substs) => (def_id, substs, None),
ty::TyGenerator(def_id, substs, interior) => (def_id, substs, Some(interior)),
_ => {
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
}
@ -448,6 +449,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
closure_id: def_id,
substs,
upvars,
interior,
}
}
@ -567,6 +569,8 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
}
hir::ExprArray(ref fields) => ExprKind::Array { fields: fields.to_ref() },
hir::ExprTup(ref fields) => ExprKind::Tuple { fields: fields.to_ref() },
hir::ExprYield(ref v) => ExprKind::Yield { value: v.to_ref() },
};
Expr {
@ -703,55 +707,64 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
});
let region = cx.tcx.mk_region(region);
let self_expr = match cx.tcx.closure_kind(closure_def_id) {
ty::ClosureKind::Fn => {
let ref_closure_ty = cx.tcx.mk_ref(region,
ty::TypeAndMut {
ty: closure_ty,
mutbl: hir::MutImmutable,
});
Expr {
ty: closure_ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::Deref {
arg: Expr {
ty: ref_closure_ty,
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,
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,
span: expr.span,
kind: ExprKind::Deref {
arg: Expr {
ty: ref_closure_ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef,
}.to_ref(),
},
}
}
ty::ClosureKind::FnOnce => {
Expr {
ty: closure_ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef,
}
}
}
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,
span: expr.span,
kind: ExprKind::Deref {
arg: Expr {
ty: ref_closure_ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef,
}.to_ref(),
},
}
}
ty::ClosureKind::FnOnce => {
Expr {
ty: closure_ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef,
}
} else {
Expr {
ty: closure_ty,
temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef,
}
};

View file

@ -61,6 +61,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
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())

View file

@ -19,7 +19,7 @@ use rustc::mir::{BinOp, BorrowKind, Field, Literal, UnOp};
use rustc::hir::def_id::DefId;
use rustc::middle::region::CodeExtent;
use rustc::ty::subst::Substs;
use rustc::ty::{self, AdtDef, ClosureSubsts, Region, Ty};
use rustc::ty::{self, AdtDef, ClosureSubsts, Region, Ty, GeneratorInterior};
use rustc::hir;
use syntax::ast;
use syntax_pos::Span;
@ -238,6 +238,7 @@ pub enum ExprKind<'tcx> {
closure_id: DefId,
substs: ClosureSubsts<'tcx>,
upvars: Vec<ExprRef<'tcx>>,
interior: Option<GeneratorInterior<'tcx>>,
},
Literal {
literal: Literal<'tcx>,
@ -247,6 +248,9 @@ pub enum ExprKind<'tcx> {
outputs: Vec<ExprRef<'tcx>>,
inputs: Vec<ExprRef<'tcx>>
},
Yield {
value: ExprRef<'tcx>,
},
}
#[derive(Clone, Debug)]

View file

@ -140,6 +140,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
LocalDecl {
mutability, ty, name: None,
source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, span },
internal: false,
is_user_variable: false
}
}
@ -160,6 +161,12 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
{
debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
// Check if this is a generator, if so, return the drop glue for it
if let Some(&ty::TyS { sty: ty::TyGenerator(gen_def_id, substs, _), .. }) = ty {
let mir = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap();
return mir.subst(tcx, substs.substs);
}
let substs = if let Some(ty) = ty {
tcx.mk_substs(iter::once(Kind::from(ty)))
} else {
@ -190,6 +197,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
),
IndexVec::new(),
sig.output(),
None,
local_decls_for_sig(&sig, span),
sig.inputs().len(),
vec![],
@ -225,10 +233,10 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
}
pub struct DropShimElaborator<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
patch: MirPatch<'tcx>,
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
pub mir: &'a Mir<'tcx>,
pub patch: MirPatch<'tcx>,
pub tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> {
@ -327,6 +335,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
),
IndexVec::new(),
self.sig.output(),
None,
self.local_decls,
self.sig.inputs().len(),
vec![],
@ -770,6 +779,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
),
IndexVec::new(),
sig.output(),
None,
local_decls,
sig.inputs().len(),
vec![],
@ -841,6 +851,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
),
IndexVec::new(),
sig.output(),
None,
local_decls,
sig.inputs().len(),
vec![],

View file

@ -60,6 +60,7 @@ impl MirPass for CopyPropagation {
return
}
}
MirSource::GeneratorDrop(_) => (),
}
// We only run when the MIR optimization level is > 1.

View file

@ -96,42 +96,42 @@ fn find_dead_unwinds<'a, 'tcx>(
MaybeInitializedLvals::new(tcx, mir, &env),
|bd, p| &bd.move_data().move_paths[p]);
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
match bb_data.terminator().kind {
let location = match bb_data.terminator().kind {
TerminatorKind::Drop { ref location, unwind: Some(_), .. } |
TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => {
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);
for stmt in 0..bb_data.statements.len() {
let loc = Location { block: bb, statement_index: stmt };
init_data.apply_location(tcx, mir, env, loc);
}
TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => location,
_ => continue,
};
let path = match env.move_data.rev_lookup.find(location) {
LookupResult::Exact(e) => e,
LookupResult::Parent(..) => {
debug!("find_dead_unwinds: has parent; skipping");
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);
for stmt in 0..bb_data.statements.len() {
let loc = Location { block: bb, statement_index: stmt };
init_data.apply_location(tcx, mir, env, loc);
}
debug!("find_dead_unwinds @ {:?}: path({:?})={:?}", bb, location, path);
let mut maybe_live = false;
on_all_drop_children_bits(tcx, mir, &env, path, |child| {
let (child_maybe_live, _) = init_data.state(child);
maybe_live |= child_maybe_live;
});
debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
if !maybe_live {
dead_unwinds.add(&bb);
}
let path = match env.move_data.rev_lookup.find(location) {
LookupResult::Exact(e) => e,
LookupResult::Parent(..) => {
debug!("find_dead_unwinds: has parent; skipping");
continue
}
_ => {}
};
debug!("find_dead_unwinds @ {:?}: path({:?})={:?}", bb, location, path);
let mut maybe_live = false;
on_all_drop_children_bits(tcx, mir, &env, path, |child| {
let (child_maybe_live, _) = init_data.state(child);
maybe_live |= child_maybe_live;
});
debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
if !maybe_live {
dead_unwinds.add(&bb);
}
}
@ -314,7 +314,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
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)
});
}

View file

@ -0,0 +1,786 @@
// 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.
//! This is the implementation of the pass which transforms generators into state machines.
//!
//! MIR generation for generators creates a function which has a self argument which
//! passes by value. This argument is effectively a generator type which only contains upvars and
//! is only used for this argument inside the MIR for the generator.
//! It is passed by value to enable upvars to be moved out of it. Drop elaboration runs on that
//! MIR before this pass and creates drop flags for MIR locals.
//! It will also drop the generator argument (which only consists of upvars) if any of the upvars
//! are moved out of. This pass elaborates the drops of upvars / generator argument in the case
//! that none of the upvars were moved out of. This is because we cannot have any drops of this
//! generator in the MIR, since it is used to create the drop glue for the generator. We'd get
//! infinite recursion otherwise.
//!
//! This pass creates the implementation for the Generator::resume function and the drop shim
//! for the generator based on the MIR input. It converts the generator argument from Self to
//! &mut Self adding derefs in the MIR as needed. It computes the final layout of the generator
//! struct which looks like this:
//! First upvars are stored
//! It is followed by the generator state field.
//! Then finally the MIR locals which are live across a suspension point are stored.
//!
//! struct Generator {
//! upvars...,
//! state: u32,
//! mir_locals...,
//! }
//!
//! This pass computes the meaning of the state field and the MIR locals which are live
//! across a suspension point. There are however two hardcoded generator states:
//! 0 - Generator have not been resumed yet
//! 1 - Generator has been poisoned
//!
//! It also rewrites `return x` and `yield y` as setting a new generator state and returning
//! GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively.
//! MIR locals which are live across a suspension point are moved to the generator struct
//! with references to them being updated with references to the generator struct.
//!
//! The pass creates two functions which have a switch on the generator state giving
//! the action to take.
//!
//! One of them is the implementation of Generator::resume.
//! For generators which have already returned it panics.
//! For generators with state 0 (unresumed) it starts the execution of the generator.
//! For generators with state 1 (poisoned) it panics.
//! Otherwise it continues the execution from the last suspension point.
//!
//! The other function is the drop glue for the generator.
//! For generators which have already returned it does nothing.
//! For generators with state 0 (unresumed) it drops the upvars of the generator.
//! For generators with state 1 (poisoned) it does nothing.
//! Otherwise it drops all the values in scope at the last suspension point.
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource};
use rustc::mir::visit::{LvalueContext, MutVisitor};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
use rustc::ty::subst::{Kind, Substs};
use util::dump_mir;
use util::liveness;
use rustc_const_math::ConstInt;
use rustc_data_structures::indexed_vec::Idx;
use std::collections::HashMap;
use std::borrow::Cow;
use std::iter::once;
use std::mem;
use transform::simplify;
use transform::no_landing_pads::no_landing_pads;
pub struct StateTransform;
struct RenameLocalVisitor {
from: Local,
to: Local,
}
impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor {
fn visit_local(&mut self,
local: &mut Local) {
if *local == self.from {
*local = self.to;
}
}
}
struct DerefArgVisitor;
impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor {
fn visit_lvalue(&mut self,
lvalue: &mut Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: Location) {
if *lvalue == Lvalue::Local(self_arg()) {
*lvalue = Lvalue::Projection(Box::new(Projection {
base: lvalue.clone(),
elem: ProjectionElem::Deref,
}));
} else {
self.super_lvalue(lvalue, context, location);
}
}
}
fn self_arg() -> Local {
Local::new(1)
}
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(self_arg());
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>) {
// 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
}
});
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
};
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(&self_arg());
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;
// Note that `elaborate_drops` only drops the upvars of a generator, and
// this is ok because `open_drop` can only be reached within that own
// generator's resume function.
let param_env = tcx.param_env(def_id);
let gen = self_arg();
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[self_arg()] = LocalDecl {
mutability: Mutability::Mut,
ty: tcx.mk_ptr(ty::TypeAndMut {
ty: gen_ty,
mutbl: hir::Mutability::MutMutable,
}),
name: None,
source_info,
internal: false,
is_user_variable: false,
};
no_landing_pads(tcx, &mut mir);
// Make sure we remove dead blocks to remove
// unrelated code from the resume part of the function
simplify::remove_dead_blocks(&mut mir);
dump_mir(tcx, None, "generator_drop", &0, source, &mut mir);
mir
}
fn insert_panic_on_resume_after_return<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &mut Mir<'tcx>) {
let assert_block = BasicBlock::new(mir.basic_blocks().len());
let term = TerminatorKind::Assert {
cond: Operand::Constant(box Constant {
span: mir.span,
ty: tcx.types.bool,
literal: Literal::Value {
value: ConstVal::Bool(false),
},
}),
expected: true,
msg: AssertMessage::GeneratorResumedAfterReturn,
target: assert_block,
cleanup: None,
};
let source_info = SourceInfo {
span: mir.span,
scope: ARGUMENT_VISIBILITY_SCOPE,
};
mir.basic_blocks_mut().push(BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
source_info,
kind: term,
}),
is_cleanup: false,
});
}
fn create_generator_resume_function<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mut transform: TransformVisitor<'a, 'tcx>,
def_id: DefId,
source: MirSource,
mir: &mut Mir<'tcx>) {
// Poison the generator when it unwinds
for block in mir.basic_blocks_mut() {
let source_info = block.terminator().source_info;
if let &TerminatorKind::Resume = &block.terminator().kind {
block.statements.push(transform.set_state(1, source_info));
}
}
let source_info = SourceInfo {
span: mir.span,
scope: ARGUMENT_VISIBILITY_SCOPE,
};
let poisoned_block = BasicBlock::new(mir.basic_blocks().len());
let term = TerminatorKind::Assert {
cond: Operand::Constant(box Constant {
span: mir.span,
ty: tcx.types.bool,
literal: Literal::Value {
value: ConstVal::Bool(false),
},
}),
expected: true,
msg: AssertMessage::GeneratorResumedAfterPanic,
target: transform.return_block,
cleanup: None,
};
mir.basic_blocks_mut().push(BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
source_info,
kind: term,
}),
is_cleanup: false,
});
transform.bb_targets.insert((poisoned_block, None), 1);
let switch = TerminatorKind::SwitchInt {
discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)),
switch_ty: tcx.types.u32,
values: Cow::from(transform.bb_targets.values().map(|&i| {
ConstInt::U32(i)
}).collect::<Vec<_>>()),
targets: transform.bb_targets.keys()
.map(|&(k, _)| k)
.chain(once(transform.return_block))
.collect(),
};
insert_entry_point(mir, BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
source_info,
kind: switch,
}),
is_cleanup: false,
});
make_generator_state_argument_indirect(tcx, def_id, mir);
no_landing_pads(tcx, 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(self_arg()),
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());
let hir_id = tcx.hir.node_to_hir_id(node_id);
// Get the interior types which typeck computed
let interior = *tcx.typeck_tables_of(def_id).generator_interiors().get(hir_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
create_generator_resume_function(tcx, transform, def_id, source, mir);
}
}

View file

@ -180,6 +180,10 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
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[..]);
@ -657,6 +661,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
self.super_terminator_kind(block, kind, loc);
match *kind {
TerminatorKind::GeneratorDrop |
TerminatorKind::Yield { .. } => bug!(),
TerminatorKind::Goto { ref mut target} => {
*target = self.update_target(*target);
}

View file

@ -40,6 +40,7 @@ pub mod dump_mir;
pub mod deaggregator;
pub mod instcombine;
pub mod copy_prop;
pub mod generator;
pub mod inline;
pub mod nll;

View file

@ -43,6 +43,8 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::Unreachable |
TerminatorKind::GeneratorDrop |
TerminatorKind::Yield { .. } |
TerminatorKind::SwitchInt { .. } => {
/* nothing to do */
},

View file

@ -392,6 +392,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
}).into_iter().collect(),
IndexVec::new(),
ty,
None,
initial_locals,
0,
vec![],

View file

@ -375,6 +375,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
TerminatorKind::SwitchInt {..} |
TerminatorKind::DropAndReplace { .. } |
TerminatorKind::Resume |
TerminatorKind::GeneratorDrop |
TerminatorKind::Yield { .. } |
TerminatorKind::Unreachable => None,
TerminatorKind::Return => {
@ -978,6 +980,7 @@ impl MirPass for QualifyAndPromoteConstants {
}
MirSource::Static(_, hir::MutImmutable) => Mode::Static,
MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
MirSource::GeneratorDrop(_) |
MirSource::Const(_) |
MirSource::Promoted(..) => return
};

View file

@ -34,7 +34,10 @@ fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
mirbug($context.tcx(), $context.last_span,
&format!("broken MIR ({:?}): {}", $elem, format!($($message)*)))
&format!("broken MIR in {:?} ({:?}): {}",
$context.body_id,
$elem,
format_args!($($message)*)))
})
}
@ -60,6 +63,7 @@ struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
last_span: Span,
body_id: ast::NodeId,
errors_reported: bool
}
@ -108,8 +112,9 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
fn new(cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
TypeVerifier {
cx,
mir,
body_id: cx.body_id,
cx,
last_span: mir.span,
errors_reported: false
}
@ -297,6 +302,19 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
})
}
}
ty::TyGenerator(def_id, substs, _) => {
// Try upvars first. `field_tys` requires final optimized MIR.
if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field.index()) {
return Ok(ty);
}
return match substs.field_tys(def_id, tcx).nth(field.index()) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: substs.field_tys(def_id, tcx).count() + 1
})
}
}
ty::TyTuple(tys, _) => {
return match tys.get(field.index()) {
Some(&ty) => Ok(ty),
@ -428,6 +446,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
TerminatorKind::Goto { .. } |
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::Drop { .. } => {
// no checks needed for these
@ -494,6 +513,22 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
}
TerminatorKind::Yield { ref value, .. } => {
let value_ty = value.ty(mir, tcx);
match mir.yield_ty {
None => span_mirbug!(self, term, "yield in non-generator"),
Some(ty) => {
if let Err(terr) = self.sub_types(value_ty, ty) {
span_mirbug!(self,
term,
"type of yield value is {:?}, but the yield type is {:?}: {:?}",
value_ty,
ty,
terr);
}
}
}
}
}
}
@ -620,6 +655,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
span_mirbug!(self, block, "return on cleanup block")
}
}
TerminatorKind::GeneratorDrop { .. } => {
if is_cleanup {
span_mirbug!(self, block, "generator_drop in cleanup block")
}
}
TerminatorKind::Yield { resume, drop, .. } => {
if is_cleanup {
span_mirbug!(self, block, "yield in cleanup block")
}
self.assert_iscleanup(mir, block, resume, is_cleanup);
if let Some(drop) = drop {
self.assert_iscleanup(mir, block, drop, is_cleanup);
}
}
TerminatorKind::Unreachable => {}
TerminatorKind::Drop { target, unwind, .. } |
TerminatorKind::DropAndReplace { target, unwind, .. } |

View file

@ -752,7 +752,14 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
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) |
// Note that `elaborate_drops` only drops the upvars of a generator,
// and this is ok because `open_drop` here can only be reached
// within that own generator's resume function.
// This should only happen for the self argument on the resume function.
// It effetively only contains upvars until the generator transformation runs.
// See librustc_mir/transform/generator.rs for more details.
ty::TyGenerator(def_id, substs, _) => {
let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
self.open_drop_for_tuple(&tys)
}

View file

@ -0,0 +1,249 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Liveness analysis which computes liveness of MIR local variables at the boundary of basic blocks
//!
//! This analysis considers references as being used only at the point of the
//! borrow. This means that this does not track uses because of references that
//! already exist:
//!
//! ```Rust
//! fn foo() {
//! x = 0;
//! // `x` is live here
//! GLOBAL = &x: *const u32;
//! // but not here, even while it can be accessed through `GLOBAL`.
//! foo();
//! x = 1;
//! // `x` is live again here, because it is assigned to `OTHER_GLOBAL`
//! OTHER_GLOBAL = &x: *const u32;
//! // ...
//! }
//! ```
//!
//! This means that users of this analysis still have to check whether
//! pre-existing references can be used to access the value (e.g. at movable
//! generator yield points, all pre-existing references are invalidated, so this
//! doesn't matter).
use rustc::mir::*;
use rustc::mir::visit::{LvalueContext, Visitor};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::indexed_set::IdxSetBuf;
use util::pretty::{write_basic_block, dump_enabled, write_mir_intro};
use rustc::mir::transform::MirSource;
use rustc::ty::item_path;
use std::path::{PathBuf, Path};
use std::fs;
use rustc::ty::TyCtxt;
use std::io::{self, Write};
pub type LocalSet = IdxSetBuf<Local>;
#[derive(Eq, PartialEq, Clone)]
struct BlockInfo {
defs: LocalSet,
uses: LocalSet,
}
struct BlockInfoVisitor {
pre_defs: LocalSet,
defs: LocalSet,
uses: LocalSet,
}
impl<'tcx> Visitor<'tcx> for BlockInfoVisitor {
fn visit_lvalue(&mut self,
lvalue: &Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: Location) {
if let Lvalue::Local(local) = *lvalue {
match context {
LvalueContext::Store |
// We let Call defined the result in both the success and unwind cases.
// This may not be right.
LvalueContext::Call |
// Storage live and storage dead aren't proper defines, but we can ignore
// values that come before them.
LvalueContext::StorageLive |
LvalueContext::StorageDead => {
self.defs.add(&local);
}
LvalueContext::Projection(..) |
// Borrows only consider their local used at the point of the borrow.
// This won't affect the results since we use this analysis for generators
// and we only care about the result at suspension points. Borrows cannot
// cross suspension points so this behavior is unproblematic.
LvalueContext::Borrow { .. } |
LvalueContext::Inspect |
LvalueContext::Consume |
LvalueContext::Validate |
// We consider drops to always be uses of locals.
// Drop eloboration should be run before this analysis otherwise
// the results might be too pessimistic.
LvalueContext::Drop => {
// Ignore uses which are already defined in this block
if !self.pre_defs.contains(&local) {
self.uses.add(&local);
}
}
}
}
self.super_lvalue(lvalue, context, location)
}
}
fn block<'tcx>(b: &BasicBlockData<'tcx>, locals: usize) -> BlockInfo {
let mut visitor = BlockInfoVisitor {
pre_defs: LocalSet::new_empty(locals),
defs: LocalSet::new_empty(locals),
uses: LocalSet::new_empty(locals),
};
let dummy_location = Location { block: BasicBlock::new(0), statement_index: 0 };
for statement in &b.statements {
visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
visitor.pre_defs.union(&visitor.defs);
}
visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location);
BlockInfo {
defs: visitor.defs,
uses: visitor.uses,
}
}
// This gives the result of the liveness analysis at the boundary of basic blocks
pub struct LivenessResult {
pub ins: IndexVec<BasicBlock, LocalSet>,
pub outs: IndexVec<BasicBlock, LocalSet>,
}
pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
let locals = mir.local_decls.len();
let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
block(b, locals)
}).collect();
let copy = |from: &IndexVec<BasicBlock, LocalSet>, to: &mut IndexVec<BasicBlock, LocalSet>| {
for (i, set) in to.iter_enumerated_mut() {
set.clone_from(&from[i]);
}
};
let mut ins: IndexVec<_, _> = mir.basic_blocks()
.indices()
.map(|_| LocalSet::new_empty(locals)).collect();
let mut outs = ins.clone();
let mut ins_ = ins.clone();
let mut outs_ = outs.clone();
loop {
copy(&ins, &mut ins_);
copy(&outs, &mut outs_);
for b in mir.basic_blocks().indices().rev() {
// out = {ins of successors}
outs[b].clear();
for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
outs[b].union(&ins[successor]);
}
// in = use (out - def)
ins[b].clone_from(&outs[b]);
ins[b].subtract(&def_use[b].defs);
ins[b].union(&def_use[b].uses);
}
if ins_ == ins && outs_ == outs {
break;
}
}
LivenessResult {
ins,
outs,
}
}
pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult) {
if !dump_enabled(tcx, pass_name, source) {
return;
}
let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
});
dump_matched_mir_node(tcx, pass_name, &node_path,
source, mir, result);
}
fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
node_path: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult) {
let mut file_path = PathBuf::new();
if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
let p = Path::new(file_dir);
file_path.push(p);
};
let file_name = format!("rustc.node{}{}-liveness.mir",
source.item_id(), pass_name);
file_path.push(&file_name);
let _ = fs::File::create(&file_path).and_then(|mut file| {
writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
writeln!(file, "// source = {:?}", source)?;
writeln!(file, "// pass_name = {}", pass_name)?;
writeln!(file, "")?;
write_mir_fn(tcx, source, mir, &mut file, result)?;
Ok(())
});
}
pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
w: &mut Write,
result: &LivenessResult)
-> io::Result<()> {
write_mir_intro(tcx, src, mir, w)?;
for block in mir.basic_blocks().indices() {
let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
let live: Vec<String> = mir.local_decls.indices()
.filter(|i| result[block].contains(i))
.map(|i| format!("{:?}", i))
.collect();
writeln!(w, "{} {{{}}}", prefix, live.join(", "))
};
print(w, " ", &result.ins)?;
write_basic_block(tcx, block, mir, w)?;
print(w, " ", &result.outs)?;
if block.index() + 1 != mir.basic_blocks().len() {
writeln!(w, "")?;
}
}
writeln!(w, "}}")?;
Ok(())
}

View file

@ -15,6 +15,7 @@ pub mod patch;
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};

View file

@ -101,6 +101,13 @@ impl<'tcx> MirPatch<'tcx> {
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);

View file

@ -94,6 +94,7 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>) {
let promotion_id = match source {
MirSource::Promoted(_, id) => format!("-{:?}", id),
MirSource::GeneratorDrop(_) => format!("-drop"),
_ => String::new()
};
@ -120,6 +121,9 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
writeln!(file, "// source = {:?}", source)?;
writeln!(file, "// pass_name = {}", pass_name)?;
writeln!(file, "// disambiguator = {}", disambiguator)?;
if let Some(ref layout) = mir.generator_layout {
writeln!(file, "// generator_layout = {:?}", layout)?;
}
writeln!(file, "")?;
write_mir_fn(tcx, source, mir, &mut file)?;
Ok(())
@ -176,7 +180,7 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
/// Write out a human-readable textual representation for the given basic block.
fn write_basic_block(tcx: TyCtxt,
pub fn write_basic_block(tcx: TyCtxt,
block: BasicBlock,
mir: &Mir,
w: &mut Write)
@ -274,7 +278,7 @@ fn write_scope_tree(tcx: TyCtxt,
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
/// local variables (both user-defined bindings and compiler temporaries).
fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir,
w: &mut Write)
@ -322,28 +326,34 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
MirSource::Const(_) => write!(w, "const")?,
MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?,
MirSource::GeneratorDrop(_) => write!(w, "drop_glue")?,
}
item_path::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere
write!(w, " {}", tcx.node_path_str(src.item_id()))
})?;
if let MirSource::Fn(_) = src {
write!(w, "(")?;
match src {
MirSource::Fn(_) | MirSource::GeneratorDrop(_) => {
write!(w, "(")?;
// fn argument types.
for (i, arg) in mir.args_iter().enumerate() {
if i != 0 {
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)
}
}
}

View file

@ -435,6 +435,9 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node
hir::ExprAgain(_) |
hir::ExprRet(_) |
// Generator expressions
hir::ExprYield(_) |
// Expressions with side-effects.
hir::ExprAssign(..) |
hir::ExprAssignOp(..) |

View file

@ -81,7 +81,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
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) => {

View file

@ -120,6 +120,8 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
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);
}
@ -131,6 +133,12 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
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);
}
@ -158,6 +166,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
AggregateKind::Tuple => "AggregateKind::Tuple",
AggregateKind::Adt(..) => "AggregateKind::Adt",
AggregateKind::Closure(..) => "AggregateKind::Closure",
AggregateKind::Generator(..) => "AggregateKind::Generator",
}, kind);
"Rvalue::Aggregate"

View file

@ -77,6 +77,12 @@ pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
if variant_index > 0 { bug!("{} is a closure, which only has one variant", t);}
substs.upvar_tys(def_id, cx.tcx()).collect()
},
ty::TyGenerator(def_id, substs, _) => {
if variant_index > 0 { bug!("{} is a generator, which only has one variant", t);}
substs.field_tys(def_id, cx.tcx()).map(|t| {
cx.tcx().normalize_associated_type(&t)
}).collect()
},
_ => bug!("{} is not a type that can have fields.", t)
}
}

View file

@ -629,6 +629,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
mir::TerminatorKind::Return |
mir::TerminatorKind::Unreachable |
mir::TerminatorKind::Assert { .. } => {}
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Yield { .. } => bug!(),
}
self.super_terminator_kind(block, kind, location);

View file

@ -28,12 +28,13 @@ use type_::Type;
use value::Value;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::{Layout, LayoutTyper};
use rustc::ty::subst::{Subst, Substs};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::hir;
use libc::{c_uint, c_char};
use std::iter;
use syntax::abi::Abi;
use syntax::attr;
use syntax::symbol::InternedString;
use syntax_pos::Span;
@ -91,6 +92,16 @@ pub fn type_pair_fields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
}
}))
}
ty::TyGenerator(def_id, substs, _) => {
let mut tys = substs.field_tys(def_id, ccx.tcx());
tys.next().and_then(|first_ty| tys.next().and_then(|second_ty| {
if tys.next().is_some() {
None
} else {
Some([first_ty, second_ty])
}
}))
}
ty::TyTuple(tys, _) => {
if tys.len() != 2 {
return None;
@ -511,6 +522,28 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
sig.abi
))
}
ty::TyGenerator(def_id, substs, _) => {
let tcx = ccx.tcx();
let sig = tcx.generator_sig(def_id).unwrap().subst(tcx, substs.substs);
let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
sig.map_bound(|sig| {
let state_did = tcx.lang_items.gen_state().unwrap();
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.mk_substs([Kind::from(sig.yield_ty),
Kind::from(sig.return_ty)].iter());
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
tcx.mk_fn_sig(iter::once(env_ty),
ret_ty,
false,
hir::Unsafety::Normal,
Abi::Rust
)
})
}
_ => bug!("unexpected type {:?} to ty_fn_sig", ty)
}
}

View file

@ -580,6 +580,16 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
unique_type_id,
usage_site_span).finalize(cx)
}
ty::TyGenerator(def_id, substs, _) => {
let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx()).map(|t| {
cx.tcx().normalize_associated_type(&t)
}).collect();
prepare_tuple_metadata(cx,
t,
&upvar_tys,
unique_type_id,
usage_site_span).finalize(cx)
}
ty::TyAdt(def, ..) => match def.adt_kind() {
AdtKind::Struct => {
prepare_struct_metadata(cx,

Some files were not shown because too many files have changed in this diff Show more