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:
commit
9a59d69323
164 changed files with 5053 additions and 471 deletions
6
src/Cargo.lock
generated
6
src/Cargo.lock
generated
|
@ -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"
|
||||
|
|
245
src/doc/unstable-book/src/language-features/generators.md
Normal file
245
src/doc/unstable-book/src/language-features/generators.md
Normal 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.
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
131
src/libcore/ops/generator.rs
Normal file
131
src/libcore/ops/generator.rs
Normal 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()
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -434,6 +434,7 @@ define_dep_nodes!( <'tcx>
|
|||
[] ImplPolarity(DefId),
|
||||
[] ClosureKind(DefId),
|
||||
[] FnSignature(DefId),
|
||||
[] GenSignature(DefId),
|
||||
[] CoerceUnsizedInfo(DefId),
|
||||
|
||||
[] ItemVarianceConstraints(DefId),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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"),
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(..) |
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>) {
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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")?;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 { .. } |
|
||||
|
|
|
@ -77,6 +77,7 @@ impl Category {
|
|||
ExprKind::Borrow { .. } |
|
||||
ExprKind::Assign { .. } |
|
||||
ExprKind::AssignOp { .. } |
|
||||
ExprKind::Yield { .. } |
|
||||
ExprKind::InlineAsm { .. } =>
|
||||
Some(Category::Rvalue(RvalueFunc::AsRvalue)),
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -126,6 +126,9 @@ pub struct Scope<'tcx> {
|
|||
|
||||
/// The cache for drop chain on “normal” exit into a particular BasicBlock.
|
||||
cached_exits: FxHashMap<(BasicBlock, CodeExtent), BasicBlock>,
|
||||
|
||||
/// The cache for drop chain on "generator drop" exit.
|
||||
cached_generator_drop: Option<BasicBlock>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -140,14 +143,22 @@ struct DropData<'tcx> {
|
|||
kind: DropKind
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
struct CachedBlock {
|
||||
/// The cached block for the cleanups-on-diverge path. This block
|
||||
/// contains code to run the current drop and all the preceding
|
||||
/// drops (i.e. those having lower index in Drop’s Scope drop
|
||||
/// array)
|
||||
unwind: Option<BasicBlock>,
|
||||
|
||||
/// The cached block for unwinds during cleanups-on-generator-drop path
|
||||
generator_drop: Option<BasicBlock>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DropKind {
|
||||
Value {
|
||||
/// The cached block for the cleanups-on-diverge path. This block
|
||||
/// contains code to run the current drop and all the preceding
|
||||
/// drops (i.e. those having lower index in Drop’s Scope drop
|
||||
/// array)
|
||||
cached_block: Option<BasicBlock>
|
||||
cached_block: CachedBlock,
|
||||
},
|
||||
Storage
|
||||
}
|
||||
|
@ -166,6 +177,29 @@ pub struct BreakableScope<'tcx> {
|
|||
pub break_destination: Lvalue<'tcx>,
|
||||
}
|
||||
|
||||
impl CachedBlock {
|
||||
fn invalidate(&mut self) {
|
||||
self.generator_drop = None;
|
||||
self.unwind = None;
|
||||
}
|
||||
|
||||
fn get(&self, generator_drop: bool) -> Option<BasicBlock> {
|
||||
if generator_drop {
|
||||
self.generator_drop
|
||||
} else {
|
||||
self.unwind
|
||||
}
|
||||
}
|
||||
|
||||
fn ref_mut(&mut self, generator_drop: bool) -> &mut Option<BasicBlock> {
|
||||
if generator_drop {
|
||||
&mut self.generator_drop
|
||||
} else {
|
||||
&mut self.unwind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DropKind {
|
||||
fn may_panic(&self) -> bool {
|
||||
match *self {
|
||||
|
@ -187,7 +221,7 @@ 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 there’s no cleanup to do at this point.
|
||||
pub fn generator_drop_cleanup(&mut self) -> Option<BasicBlock> {
|
||||
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Fill in the cache
|
||||
self.diverge_cleanup_gen(true);
|
||||
|
||||
let src_info = self.scopes[0].source_info(self.fn_span);
|
||||
let 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 there’s no
|
||||
/// cleanup to do at this point.
|
||||
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
|
||||
self.diverge_cleanup_gen(false)
|
||||
}
|
||||
|
||||
fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> Option<BasicBlock> {
|
||||
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
|
||||
return None;
|
||||
}
|
||||
|
@ -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 there’s no `cached_block`s within current scope,
|
||||
// we must look for one in the enclosing scope.
|
||||
let on_diverge = on_diverge.or_else(|| {
|
||||
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
|
||||
earlier_scopes.iter().rev().flat_map(|s| s.cached_block(generator_drop)).next()
|
||||
});
|
||||
let next = cfg.start_new_block();
|
||||
cfg.terminate(block, source_info, TerminatorKind::Drop {
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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![],
|
||||
|
|
|
@ -60,6 +60,7 @@ impl MirPass for CopyPropagation {
|
|||
return
|
||||
}
|
||||
}
|
||||
MirSource::GeneratorDrop(_) => (),
|
||||
}
|
||||
|
||||
// We only run when the MIR optimization level is > 1.
|
||||
|
|
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
786
src/librustc_mir/transform/generator.rs
Normal file
786
src/librustc_mir/transform/generator.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
},
|
||||
|
|
|
@ -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![],
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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, .. } |
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
249
src/librustc_mir/util/liveness.rs
Normal file
249
src/librustc_mir/util/liveness.rs
Normal 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(())
|
||||
}
|
||||
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(..) |
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue