Generator literal support

This commit is contained in:
John Kåre Alsaker 2016-12-26 14:34:03 +01:00
parent 6f815ca771
commit d861982ca6
127 changed files with 3238 additions and 259 deletions

View file

@ -0,0 +1,38 @@
// 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.
#[derive(Debug)]
#[cfg(not(stage0))]
#[lang = "generator_state"]
#[unstable(feature = "generator_trait", issue = "0")]
pub enum State<Y, R> {
/// The generator suspended with a value.
Yielded(Y),
/// The generator completed with a return value.
Complete(R),
}
/// The trait implemented by builtin generator types.
#[cfg(not(stage0))]
#[lang = "generator"]
#[unstable(feature = "generator_trait", issue = "0")]
#[fundamental]
pub trait Generator<Arg = ()> {
/// The type of value this generator yields.
type Yield;
/// The type of value this generator returns.
type Return;
/// This resumes the execution of the generator.
fn resume(&mut self, arg: Arg) -> State<Self::Yield, Self::Return>;
}

View file

@ -152,6 +152,7 @@ mod bit;
mod deref;
mod drop;
mod function;
mod generator;
mod index;
mod place;
mod range;
@ -189,6 +190,13 @@ pub use self::range::{RangeInclusive, RangeToInclusive};
#[unstable(feature = "try_trait", issue = "42327")]
pub use self::try::Try;
#[unstable(feature = "generator_trait", issue = "0")]
#[cfg(not(stage0))]
pub use self::generator::State;
#[unstable(feature = "generator_trait", issue = "0")]
#[cfg(not(stage0))]
pub use self::generator::Generator;
#[unstable(feature = "placement_new_protocol", issue = "27779")]
pub use self::place::{Place, Placer, InPlace, Boxed, BoxPlace};

View file

@ -389,6 +389,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
hir::ExprUnary(_, ref e) |
hir::ExprField(ref e, _) |
hir::ExprTupField(ref e, _) |
hir::ExprSuspend(ref e) |
hir::ExprRepeat(ref e, _) => {
self.straightline(expr, pred, Some(&**e).into_iter())
}
@ -401,6 +402,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
hir::ExprClosure(..) |
hir::ExprLit(..) |
hir::ExprImplArg(_) |
hir::ExprPath(_) => {
self.straightline(expr, pred, None::<hir::Expr>.iter())
}

View file

@ -399,6 +399,9 @@ pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body) {
visitor.visit_id(argument.id);
visitor.visit_pat(&argument.pat);
}
if let Some(ref impl_arg) = body.impl_arg {
visitor.visit_id(impl_arg.id);
}
visitor.visit_expr(&body.value);
}
@ -978,7 +981,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
visitor.visit_expr(subexpression);
walk_list!(visitor, visit_arm, arms);
}
ExprClosure(_, ref function_declaration, body, _fn_decl_span) => {
ExprClosure(_, ref function_declaration, body, _fn_decl_span, _gen) => {
visitor.visit_fn(FnKind::Closure(&expression.attrs),
function_declaration,
body,
@ -1042,6 +1045,12 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
visitor.visit_expr(input)
}
}
ExprSuspend(ref subexpression) => {
visitor.visit_expr(subexpression);
}
ExprImplArg(id) => {
visitor.visit_id(id);
},
}
}

View file

@ -92,6 +92,8 @@ pub struct LoweringContext<'a> {
trait_impls: BTreeMap<DefId, Vec<NodeId>>,
trait_default_impl: BTreeMap<DefId, NodeId>,
impl_arg: Option<NodeId>,
catch_scopes: Vec<NodeId>,
loop_scopes: Vec<NodeId>,
is_in_loop_condition: bool,
@ -137,6 +139,7 @@ pub fn lower_crate(sess: &Session,
trait_impls: BTreeMap::new(),
trait_default_impl: BTreeMap::new(),
exported_macros: Vec::new(),
impl_arg: None,
catch_scopes: Vec::new(),
loop_scopes: Vec::new(),
is_in_loop_condition: false,
@ -362,12 +365,24 @@ impl<'a> LoweringContext<'a> {
})
}
fn impl_arg_id(&mut self) -> NodeId {
if self.impl_arg.is_none() {
self.impl_arg = Some(self.next_id());
}
self.impl_arg.unwrap()
}
fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>)
-> hir::BodyId {
let span = value.span;
let body = hir::Body {
arguments: decl.map_or(hir_vec![], |decl| {
decl.inputs.iter().map(|x| self.lower_arg(x)).collect()
}),
impl_arg: self.impl_arg.map(|id| hir::ImplArg {
id,
span,
}),
value,
};
let id = body.id();
@ -425,6 +440,17 @@ 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 old_impl_arg = self.impl_arg;
self.impl_arg = None;
let result = f(self);
let r = self.record_body(result, decl);
self.impl_arg = old_impl_arg;
r
}
fn with_loop_scope<T, F>(&mut self, loop_id: NodeId, f: F) -> T
where F: FnOnce(&mut LoweringContext) -> T
{
@ -609,13 +635,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 body = self.lower_body(None, |this| this.lower_expr(length));
hir::TyArray(self.lower_ty(ty), body)
}
TyKind::Typeof(ref expr) => {
let expr = self.lower_expr(expr);
hir::TyTypeof(self.record_body(expr, None))
let body = self.lower_body(None, |this| this.lower_expr(expr));
hir::TyTypeof(body)
}
TyKind::TraitObject(ref bounds) => {
let mut lifetime_bound = None;
@ -672,8 +697,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,
@ -1287,21 +1311,21 @@ impl<'a> LoweringContext<'a> {
hir::ItemUse(path, kind)
}
ItemKind::Static(ref t, m, ref e) => {
let value = self.lower_expr(e);
let body = self.lower_body(None, |this| this.lower_expr(e));
hir::ItemStatic(self.lower_ty(t),
self.lower_mutability(m),
self.record_body(value, None))
body)
}
ItemKind::Const(ref t, ref e) => {
let value = self.lower_expr(e);
hir::ItemConst(self.lower_ty(t),
self.record_body(value, None))
let body = self.lower_body(None, |this| this.lower_expr(e));
hir::ItemConst(self.lower_ty(t), body)
}
ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
self.with_new_scopes(|this| {
let body = this.lower_block(body, false);
let body = this.expr_block(body, ThinVec::new());
let body_id = this.record_body(body, Some(decl));
let body_id = this.lower_body(Some(decl), |this| {
let body = this.lower_block(body, false);
this.expr_block(body, ThinVec::new())
});
hir::ItemFn(this.lower_fn_decl(decl),
this.lower_unsafety(unsafety),
this.lower_constness(constness),
@ -1394,8 +1418,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) => {
@ -1404,9 +1427,10 @@ impl<'a> LoweringContext<'a> {
hir::TraitMethod::Required(names))
}
TraitItemKind::Method(ref sig, Some(ref body)) => {
let body_id = this.lower_body(Some(&sig.decl), |this| {
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));
this.expr_block(body, ThinVec::new())
});
hir::TraitItemKind::Method(this.lower_method_sig(sig),
hir::TraitMethod::Provided(body_id))
}
@ -1455,14 +1479,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_id = this.lower_body(Some(&sig.decl), |this| {
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));
this.expr_block(body, ThinVec::new())
});
hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
}
ImplItemKind::Type(ref ty) => hir::ImplItemKind::Type(this.lower_ty(ty)),
@ -1834,8 +1858,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 body = self.lower_body(None, |this| this.lower_expr(count));
hir::ExprRepeat(expr, body)
}
ExprKind::Tup(ref elts) => {
hir::ExprTup(elts.iter().map(|x| self.lower_expr(x)).collect())
@ -1928,11 +1952,17 @@ 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 gen = None;
let body_id = this.lower_body(Some(decl), |this| {
let e = this.lower_expr(body);
gen = this.impl_arg.map(|_| hir::GeneratorClause::Movable);
e
});
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,
gen)
})
})
}
@ -2068,6 +2098,18 @@ impl<'a> LoweringContext<'a> {
return ex;
}
ExprKind::Yield(ref opt_expr) => {
self.impl_arg_id();
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::ExprSuspend(P(expr))
}
ExprKind::ImplArg => {
hir::ExprImplArg(self.impl_arg_id())
}
// Desugar ExprIfLet
// From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
ExprKind::IfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {

View file

@ -252,7 +252,8 @@ 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) =>
// FIXME: Does this need handling for generators?
closure(ClosureParts::new(&decl, block, e.id, e.span, &e.attrs)),
_ => bug!("expr FnLikeNode that is not fn-like"),
},

View file

@ -182,6 +182,13 @@ impl<'hir> Visitor<'hir> for NodeCollector<'hir> {
});
}
fn visit_body(&mut self, b: &'hir Body) {
if let Some(ref impl_arg) = b.impl_arg {
self.insert(impl_arg.id, NodeImplArg(impl_arg));
}
intravisit::walk_body(self, b);
}
fn visit_fn(&mut self, fk: intravisit::FnKind<'hir>, fd: &'hir FnDecl,
b: BodyId, s: Span, id: NodeId) {
assert_eq!(self.parent_node, id);

View file

@ -55,6 +55,7 @@ pub enum Node<'hir> {
NodeTraitRef(&'hir TraitRef),
NodeLocal(&'hir Pat),
NodePat(&'hir Pat),
NodeImplArg(&'hir ImplArg),
NodeBlock(&'hir Block),
/// NodeStructCtor represents a tuple struct.
@ -84,6 +85,7 @@ enum MapEntry<'hir> {
EntryTy(NodeId, &'hir Ty),
EntryTraitRef(NodeId, &'hir TraitRef),
EntryLocal(NodeId, &'hir Pat),
EntryImplArg(NodeId, &'hir ImplArg),
EntryPat(NodeId, &'hir Pat),
EntryBlock(NodeId, &'hir Block),
EntryStructCtor(NodeId, &'hir VariantData),
@ -115,6 +117,7 @@ impl<'hir> MapEntry<'hir> {
NodeTy(n) => EntryTy(p, n),
NodeTraitRef(n) => EntryTraitRef(p, n),
NodeLocal(n) => EntryLocal(p, n),
NodeImplArg(n) => EntryImplArg(p, n),
NodePat(n) => EntryPat(p, n),
NodeBlock(n) => EntryBlock(p, n),
NodeStructCtor(n) => EntryStructCtor(p, n),
@ -136,6 +139,7 @@ impl<'hir> MapEntry<'hir> {
EntryStmt(id, _) => id,
EntryTy(id, _) => id,
EntryTraitRef(id, _) => id,
EntryImplArg(id, _) => id,
EntryLocal(id, _) => id,
EntryPat(id, _) => id,
EntryBlock(id, _) => id,
@ -162,13 +166,16 @@ impl<'hir> MapEntry<'hir> {
EntryTy(_, n) => NodeTy(n),
EntryTraitRef(_, n) => NodeTraitRef(n),
EntryLocal(_, n) => NodeLocal(n),
EntryImplArg(_, n) => NodeImplArg(n),
EntryPat(_, n) => NodePat(n),
EntryBlock(_, n) => NodeBlock(n),
EntryStructCtor(_, n) => NodeStructCtor(n),
EntryLifetime(_, n) => NodeLifetime(n),
EntryTyParam(_, n) => NodeTyParam(n),
EntryVisibility(_, n) => NodeVisibility(n),
_ => return None
NotPresent |
RootCrate => return None
})
}
@ -201,7 +208,7 @@ impl<'hir> MapEntry<'hir> {
EntryExpr(_, expr) => {
match expr.node {
ExprClosure(.., body, _) => Some(body),
ExprClosure(.., body, _, _) => Some(body),
_ => None,
}
}
@ -320,6 +327,7 @@ impl<'hir> Map<'hir> {
EntryTy(p, _) |
EntryTraitRef(p, _) |
EntryLocal(p, _) |
EntryImplArg(p, _) |
EntryPat(p, _) |
EntryBlock(p, _) |
EntryStructCtor(p, _) |
@ -895,6 +903,7 @@ impl<'hir> Map<'hir> {
Some(EntryTy(_, ty)) => ty.span,
Some(EntryTraitRef(_, tr)) => tr.path.span,
Some(EntryLocal(_, pat)) => pat.span,
Some(EntryImplArg(_, impl_arg)) => impl_arg.span,
Some(EntryPat(_, pat)) => pat.span,
Some(EntryBlock(_, block)) => block.span,
Some(EntryStructCtor(_, _)) => self.expect_item(self.get_parent(id)).span,
@ -1104,6 +1113,7 @@ impl<'a> print::State<'a> {
}
NodeLifetime(a) => self.print_lifetime(&a),
NodeVisibility(a) => self.print_visibility(&a),
NodeImplArg(_) => bug!("cannot print ImplArg"),
NodeTyParam(_) => bug!("cannot print TyParam"),
NodeField(_) => bug!("cannot print StructField"),
// these cases do not carry enough information in the
@ -1205,6 +1215,9 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
Some(NodeLocal(_)) => {
format!("local {}{}", map.node_to_pretty_string(id), id_str)
}
Some(NodeImplArg(_)) => {
format!("impl_arg {}{}", map.node_to_pretty_string(id), id_str)
}
Some(NodePat(_)) => {
format!("pat {}{}", map.node_to_pretty_string(id), id_str)
}

View file

@ -925,6 +925,13 @@ pub enum UnsafeSource {
UserProvided,
}
/// represents an implicit argument of a generator
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct ImplArg {
pub id: NodeId,
pub span: Span,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct BodyId {
pub node_id: NodeId,
@ -934,7 +941,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 impl_arg: Option<ImplArg>,
}
impl Body {
@ -943,6 +951,10 @@ impl Body {
node_id: self.value.id
}
}
pub fn is_generator(&self) -> bool {
self.impl_arg.is_some()
}
}
/// An expression
@ -1011,7 +1023,7 @@ 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),
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, Option<GeneratorClause>),
/// A block (`{ ... }`)
ExprBlock(P<Block>),
@ -1056,6 +1068,12 @@ 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
ExprSuspend(P<Expr>),
/// The argument to a generator
ExprImplArg(NodeId),
}
/// Optionally `Self`-qualified value/type path or associated extension.
@ -1184,6 +1202,12 @@ pub struct Destination {
pub target_id: ScopeTarget,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum GeneratorClause {
Immovable,
Movable,
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum CaptureClause {
CaptureByValue,

View file

@ -1312,7 +1312,12 @@ 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) => {
if gen.is_some() {
self.head("gen")?;
space(&mut self.s)?;
}
self.print_capture_clause(capture_clause)?;
self.print_closure_args(&decl, body)?;
@ -1461,6 +1466,15 @@ impl<'a> State<'a> {
self.pclose()?;
}
hir::ExprSuspend(ref expr) => {
word(&mut self.s, "suspend ")?;
self.print_expr(&expr)?;
}
hir::ExprImplArg(_) => {
word(&mut self.s, "gen")?;
space(&mut self.s)?;
word(&mut self.s, "arg")?;
}
}
self.ann.post(self, NodeExpr(expr))?;
self.end()

View file

@ -573,6 +573,8 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::E
hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
hir::ExprSuspend(..) |
hir::ExprImplArg(..) |
hir::ExprInlineAsm(..) |
hir::ExprRepeat(..) |
hir::ExprTup(..) => {
@ -637,7 +639,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),
@ -651,7 +653,9 @@ impl_stable_hash_for!(enum hir::Expr_ {
ExprRet(val),
ExprInlineAsm(asm, inputs, outputs),
ExprStruct(path, fields, base),
ExprRepeat(val, times)
ExprRepeat(val, times),
ExprSuspend(val),
ExprImplArg(id)
});
impl_stable_hash_for!(enum hir::LocalSource {
@ -686,6 +690,11 @@ impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::M
}
}
impl_stable_hash_for!(enum hir::GeneratorClause {
Immovable,
Movable
});
impl_stable_hash_for!(enum hir::CaptureClause {
CaptureByValue,
CaptureByRef
@ -1022,9 +1031,15 @@ impl_stable_hash_for!(struct hir::Arg {
id
});
impl_stable_hash_for!(struct hir::ImplArg {
id,
span
});
impl_stable_hash_for!(struct hir::Body {
arguments,
value
value,
impl_arg
});
impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for hir::BodyId {

View file

@ -17,7 +17,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
StableHasherResult};
use std::mem;
impl_stable_hash_for!(struct mir::GeneratorLayout<'tcx> { fields });
impl_stable_hash_for!(struct mir::SourceInfo { span, scope });
impl_stable_hash_for!(enum mir::Mutability { Mut, Not });
impl_stable_hash_for!(enum mir::BorrowKind { Shared, Unique, Mut });
@ -54,9 +54,11 @@ for mir::Terminator<'tcx> {
mir::TerminatorKind::SwitchInt { .. } |
mir::TerminatorKind::Resume |
mir::TerminatorKind::Return |
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Unreachable |
mir::TerminatorKind::Drop { .. } |
mir::TerminatorKind::DropAndReplace { .. } |
mir::TerminatorKind::Suspend { .. } |
mir::TerminatorKind::Call { .. } => false,
};
@ -146,6 +148,7 @@ for mir::TerminatorKind<'tcx> {
}
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 +164,13 @@ for mir::TerminatorKind<'tcx> {
target.hash_stable(hcx, hasher);
unwind.hash_stable(hcx, hasher);
}
mir::TerminatorKind::Suspend { 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 +210,8 @@ for mir::AssertMessage<'tcx> {
mir::AssertMessage::Math(ref const_math_err) => {
const_math_err.hash_stable(hcx, hasher);
}
mir::AssertMessage::GeneratorResumedAfterReturn => (),
mir::AssertMessage::GeneratorResumedAfterPanic => (),
}
}
}
@ -406,7 +418,8 @@ for mir::AggregateKind<'tcx> {
substs.hash_stable(hcx, hasher);
active_field.hash_stable(hcx, hasher);
}
mir::AggregateKind::Closure(def_id, ref substs) => {
mir::AggregateKind::Closure(def_id, ref substs) |
mir::AggregateKind::Generator(def_id, ref substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}

View file

@ -142,6 +142,12 @@ for ty::UpvarCapture<'tcx> {
}
}
impl_stable_hash_for!(struct ty::GenSig<'tcx> {
impl_arg_ty,
suspend_ty,
return_ty
});
impl_stable_hash_for!(struct ty::FnSig<'tcx> {
inputs_and_output,
variadic,
@ -315,6 +321,8 @@ for ::middle::const_val::ConstVal<'tcx> {
impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs });
impl_stable_hash_for!(tuple_struct ty::GeneratorInterior<'tcx> { ty });
impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> {
parent,
predicates
@ -541,6 +549,12 @@ for ty::TypeVariants<'tcx>
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);
@ -620,6 +634,8 @@ for ty::TypeckTables<'tcx> {
ref upvar_capture_map,
ref closure_tys,
ref closure_kinds,
ref generator_interiors,
ref generator_sigs,
ref liberated_fn_sigs,
ref fru_field_types,
@ -650,6 +666,8 @@ for ty::TypeckTables<'tcx> {
ich::hash_stable_nodemap(hcx, hasher, closure_tys);
ich::hash_stable_nodemap(hcx, hasher, closure_kinds);
ich::hash_stable_nodemap(hcx, hasher, generator_interiors);
ich::hash_stable_nodemap(hcx, hasher, generator_sigs);
ich::hash_stable_nodemap(hcx, hasher, liberated_fn_sigs);
ich::hash_stable_nodemap(hcx, hasher, fru_field_types);
ich::hash_stable_nodemap(hcx, hasher, cast_kinds);

View file

@ -16,6 +16,7 @@ use ty::{self, Ty, TyInfer, TyVar};
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax_pos::DUMMY_SP;
struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
@ -23,6 +24,7 @@ struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
hir_map: &'a hir::map::Map<'gcx>,
found_local_pattern: Option<&'gcx Pat>,
found_arg_pattern: Option<&'gcx Pat>,
found_impl_arg: bool,
}
impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
@ -68,6 +70,11 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> {
self.found_arg_pattern = Some(&*argument.pat);
}
}
if let Some(ref impl_arg) = body.impl_arg {
if !self.found_impl_arg && self.node_matches_type(impl_arg.id) {
self.found_impl_arg = true;
}
}
intravisit::walk_body(self, body);
}
}
@ -101,6 +108,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
hir_map: &self.tcx.hir,
found_local_pattern: None,
found_arg_pattern: None,
found_impl_arg: false,
};
if let Some(body_id) = body_id {
@ -137,6 +145,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
if local_visitor.found_impl_arg {
labels.push((DUMMY_SP, format!("consider giving a type to the implicit generator argument")));
}
let mut err = struct_span_err!(self.tcx.sess,
err_span,
E0282,

View file

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

View file

@ -992,6 +992,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.next_region_var(EarlyBoundRegion(span, def.name, def.issue_32330))
}
pub fn type_var_for_impl_arg(&self,
span: Span,
def_id: DefId)
-> Ty<'tcx> {
let default = Some(type_variable::Default {
ty: self.tcx.mk_nil(),
origin_span: span,
def_id: def_id,
});
let ty_var_id = self.type_variables
.borrow_mut()
.new_var(false,
TypeVariableOrigin::TypeInference(span),
default);
self.tcx.mk_var(ty_var_id)
}
/// Create a type inference variable for the given
/// type parameter definition. The substitutions are
/// for actual parameters that may be referred to by
@ -1362,6 +1381,18 @@ 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) {
if let Some(&ty) = tables.borrow().generator_sigs.get(&id) {
return ty.map(|t| ty::Binder(t));
}
}
}
self.tcx.generator_sig(def_id)
}
}
impl<'a, 'gcx, 'tcx> TypeTrace<'tcx> {

View file

@ -517,13 +517,19 @@ 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::ExprSuspend(ref value) => {
self.consume_expr(&value);
}
hir::ExprImplArg(_) => { }
}
}

View file

@ -315,6 +315,9 @@ language_item_table! {
FnMutTraitLangItem, "fn_mut", fn_mut_trait;
FnOnceTraitLangItem, "fn_once", fn_once_trait;
GeneratorStateLangItem, "generator_state", gen_state;
GeneratorTraitLangItem, "generator", gen_trait;
EqTraitLangItem, "eq", eq_trait;
OrdTraitLangItem, "ord", ord_trait;

View file

@ -249,6 +249,7 @@ struct LocalInfo {
#[derive(Copy, Clone, Debug)]
enum VarKind {
ImplArg(NodeId),
Arg(NodeId, ast::Name),
Local(LocalInfo),
CleanExit
@ -304,7 +305,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
self.num_vars += 1;
match vk {
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) | ImplArg(node_id) => {
self.variable_map.insert(node_id, v);
},
CleanExit => {}
@ -329,6 +330,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
Local(LocalInfo { name, .. }) | Arg(_, name) => {
name.to_string()
},
ImplArg(_) => "<impl-arg>".to_string(),
CleanExit => "<clean-exit>".to_string()
}
}
@ -365,6 +367,10 @@ fn visit_fn<'a, 'tcx: 'a>(ir: &mut IrMaps<'a, 'tcx>,
})
};
if let Some(ref impl_arg) = body.impl_arg {
fn_maps.add_variable(ImplArg(impl_arg.id));
}
// gather up the various local variables, significant expressions,
// and so forth:
intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id);
@ -417,6 +423,10 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
}
intravisit::walk_expr(ir, expr);
}
hir::ExprImplArg(_) => {
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
intravisit::walk_expr(ir, expr);
}
hir::ExprClosure(..) => {
// Interesting control flow (for loops can contain labeled
// breaks or continues)
@ -460,7 +470,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::ExprSuspend(..) |
hir::ExprType(..) | hir::ExprPath(hir::QPath::TypeRelative(..)) => {
intravisit::walk_expr(ir, expr);
}
@ -881,6 +891,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
match expr.node {
// Interesting cases with control flow or which gen/kill
hir::ExprImplArg(arg_id) => {
self.access_var(expr.id, arg_id, succ, ACC_READ | ACC_USE, expr.span)
}
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
self.access_path(expr.id, path, succ, ACC_READ | ACC_USE)
@ -894,7 +907,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 +1129,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprCast(ref e, _) |
hir::ExprType(ref e, _) |
hir::ExprUnary(_, ref e) |
hir::ExprSuspend(ref e) |
hir::ExprRepeat(ref e, _) => {
self.propagate_through_expr(&e, succ)
}
@ -1212,6 +1226,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
-> LiveNode {
match expr.node {
hir::ExprImplArg(arg_id) => {
self.access_var(expr.id, arg_id, succ, acc, expr.span)
}
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
self.access_path(expr.id, path, succ, acc)
}
@ -1224,18 +1241,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
}
@ -1397,8 +1419,8 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
hir::ExprCast(..) | hir::ExprUnary(..) | hir::ExprRet(..) |
hir::ExprBreak(..) | hir::ExprAgain(..) | hir::ExprLit(_) |
hir::ExprBlock(..) | hir::ExprAddrOf(..) |
hir::ExprStruct(..) | hir::ExprRepeat(..) |
hir::ExprClosure(..) | hir::ExprPath(_) |
hir::ExprStruct(..) | hir::ExprRepeat(..) | hir::ExprImplArg(_) |
hir::ExprClosure(..) | hir::ExprPath(_) | hir::ExprSuspend(..) |
hir::ExprBox(..) | hir::ExprType(..) => {
intravisit::walk_expr(this, expr);
}
@ -1420,6 +1442,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.warn_about_dead_assign(expr.span, expr.id, ln, var);
}
}
hir::ExprImplArg(_) => bug!(),
_ => {
// For other kinds of lvalues, no checks are required,
// and any embedded expressions are actually rvalues

View file

@ -602,6 +602,17 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
self.cat_def(expr.id, expr.span, expr_ty, def)
}
hir::ExprImplArg(id) => {
Ok(Rc::new(cmt_ {
id: expr.id,
span: expr.span,
cat: Categorization::Local(id),
mutbl: MutabilityCategory::McDeclared,
ty: expr_ty,
note: NoteNone
}))
},
hir::ExprType(ref e, _) => {
self.cat_expr(&e)
}
@ -609,7 +620,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::ExprSuspend(..) |
hir::ExprMethodCall(..) | hir::ExprCast(..) |
hir::ExprArray(..) | hir::ExprTup(..) | hir::ExprIf(..) |
hir::ExprBinary(..) | hir::ExprWhile(..) |
@ -703,7 +714,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
let kind = match self.tables.closure_kinds.get(&fn_node_id) {
Some(&(kind, _)) => kind,
None => span_bug!(span, "missing closure kind")
None => {
let ty = self.node_ty(fn_node_id)?;
match ty.sty {
ty::TyGenerator(..) => ty::ClosureKind::FnOnce,
_ => span_bug!(span, "missing closure kind"),
}
}
};
let upvar_id = ty::UpvarId { var_id,

View file

@ -32,6 +32,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 +790,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);
}
@ -1071,7 +1072,10 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
for argument in &body.arguments {
self.visit_pat(&argument.pat);
}
if let Some(ref impl_arg) = body.impl_arg {
record_var_lifetime(self, impl_arg.id, impl_arg.span);
}
// The body of the every fn is a root scope.
self.cx.parent = self.cx.var_parent;
self.visit_expr(&body.value);
@ -1142,6 +1146,66 @@ fn region_maps<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
Rc::new(maps)
}
struct YieldFinder(bool);
impl<'tcx> Visitor<'tcx> for YieldFinder {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_body(&mut self, _body: &'tcx hir::Body) {
// Closures don't execute
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
if let hir::ExprSuspend(..) = expr.node {
self.0 = true;
}
intravisit::walk_expr(self, expr);
}
}
pub fn extent_has_yield<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(tcx: TyCtxt<'a, 'gcx, 'tcx>, extent: CodeExtent) -> bool {
let mut finder = YieldFinder(false);
match extent {
CodeExtent::DestructionScope(node_id) |
CodeExtent::Misc(node_id) => {
match tcx.hir.get(node_id) {
Node::NodeItem(_) |
Node::NodeTraitItem(_) |
Node::NodeImplItem(_) => {
let body = tcx.hir.body(tcx.hir.body_owned_by(node_id));
intravisit::walk_body(&mut finder, body);
}
Node::NodeExpr(expr) => intravisit::walk_expr(&mut finder, expr),
Node::NodeStmt(stmt) => intravisit::walk_stmt(&mut finder, stmt),
Node::NodeBlock(block) => intravisit::walk_block(&mut finder, block),
_ => bug!(),
}
}
CodeExtent::CallSiteScope(body_id) |
CodeExtent::ParameterScope(body_id) => {
intravisit::walk_body(&mut finder, tcx.hir.body(body_id))
}
CodeExtent::Remainder(r) => {
if let Node::NodeBlock(block) = tcx.hir.get(r.block) {
for stmt in &block.stmts[(r.first_statement_index as usize + 1)..] {
intravisit::walk_stmt(&mut finder, stmt);
}
block.expr.as_ref().map(|e| intravisit::walk_expr(&mut finder, e));
} else {
bug!()
}
}
}
finder.0
}
pub fn provide(providers: &mut Providers) {
*providers = Providers {
region_maps,

View file

@ -104,6 +104,15 @@ pub struct Mir<'tcx> {
/// Return type of the function.
pub return_ty: Ty<'tcx>,
/// Suspend type of the function, if it is a generator.
pub suspend_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>,
suspend_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,
suspend_ty,
generator_drop: None,
generator_layout: None,
local_decls,
arg_count,
upvar_decls,
@ -168,6 +181,10 @@ impl<'tcx> Mir<'tcx> {
}
}
pub fn impl_arg_lvalue() -> Lvalue<'tcx> {
Lvalue::Local(Local::new(1))
}
#[inline]
pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
&self.basic_blocks
@ -270,6 +287,9 @@ impl_stable_hash_for!(struct Mir<'tcx> {
visibility_scopes,
promoted,
return_ty,
suspend_ty,
generator_drop,
generator_layout,
local_decls,
arg_count,
upvar_decls,
@ -567,7 +587,20 @@ pub enum TerminatorKind<'tcx> {
msg: AssertMessage<'tcx>,
target: BasicBlock,
cleanup: Option<BasicBlock>
}
},
/// A suspend point
Suspend {
/// 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 +630,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 +638,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(),
Suspend { resume: t, drop: Some(c), .. } => vec![t, c].into_cow(),
Suspend { 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 +660,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![],
Suspend { resume: ref mut t, drop: Some(ref mut c), .. } => vec![t, c],
Suspend { 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 +701,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 +748,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"),
Suspend { 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 +784,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 +801,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 +818,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![],
Suspend { drop: Some(_), .. } =>
vec!["resume".into_cow(), "drop".into_cow()],
Suspend { drop: None, .. } => vec!["resume".into_cow()],
DropAndReplace { unwind: None, .. } |
Drop { unwind: None, .. } => vec!["return".into_cow()],
DropAndReplace { unwind: Some(_), .. } |
@ -784,7 +840,9 @@ pub enum AssertMessage<'tcx> {
len: Operand<'tcx>,
index: Operand<'tcx>
},
Math(ConstMathErr)
Math(ConstMathErr),
GeneratorResumedAfterReturn,
GeneratorResumedAfterPanic,
}
///////////////////////////////////////////////////////////////////////////
@ -1120,6 +1178,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>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
@ -1281,6 +1340,30 @@ 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]")
}
}),
}
}
}
@ -1425,6 +1508,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
@ -1437,6 +1525,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),
suspend_ty: self.suspend_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(),
@ -1448,12 +1539,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.suspend_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 {
@ -1558,6 +1664,11 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
target,
unwind,
},
Suspend { ref value, resume, drop } => Suspend {
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)
@ -1587,6 +1698,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
cleanup,
}
},
GeneratorDrop => GeneratorDrop,
Resume => Resume,
Return => Return,
Unreachable => Unreachable,
@ -1606,6 +1718,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),
Suspend { 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)
@ -1626,6 +1740,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
Goto { .. } |
Resume |
Return |
GeneratorDrop |
Unreachable => false
}
}
@ -1671,7 +1786,9 @@ 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) =>
AggregateKind::Generator(id, substs.fold_with(folder)),
};
Aggregate(kind, fields.fold_with(folder))
}
@ -1697,7 +1814,8 @@ 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) => substs.visit_with(visitor),
}) || fields.visit_with(visitor)
}
}

View file

@ -202,6 +202,11 @@ impl<'tcx> Rvalue<'tcx> {
AggregateKind::Closure(did, substs) => {
tcx.mk_closure_from_closure_substs(did, substs)
}
AggregateKind::Generator(did, substs) => {
let node_id = tcx.hir.as_local_node_id(did).unwrap();
let interior = *tcx.typeck_tables_of(did).generator_interiors.get(&node_id).unwrap();
tcx.mk_generator(did, substs, interior.subst(tcx, substs.substs))
}
}
}
}

View file

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

View file

@ -247,6 +247,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);
@ -395,7 +399,8 @@ macro_rules! make_mir_visitor {
}
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => {
}
@ -442,6 +447,15 @@ macro_rules! make_mir_visitor {
self.visit_branch(block, target);
cleanup.map(|t| self.visit_branch(block, t));
}
TerminatorKind::Suspend { ref $($mutability)* value,
resume,
drop } => {
self.visit_operand(value, source_location);
self.visit_branch(block, resume);
drop.map(|t| self.visit_branch(block, t));
}
}
}
@ -456,7 +470,9 @@ macro_rules! make_mir_visitor {
self.visit_operand(len, location);
self.visit_operand(index, location);
}
AssertMessage::Math(_) => {}
AssertMessage::Math(_) => {},
AssertMessage::GeneratorResumedAfterReturn => {},
AssertMessage::GeneratorResumedAfterPanic => {},
}
}
@ -534,6 +550,11 @@ macro_rules! make_mir_visitor {
self.visit_def_id(def_id, location);
self.visit_closure_substs(closure_substs);
}
AggregateKind::Generator(ref $($mutability)* def_id,
ref $($mutability)* closure_substs) => {
self.visit_def_id(def_id, location);
self.visit_closure_substs(closure_substs);
}
}
for operand in operands {
@ -561,7 +582,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);

View file

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

View file

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

View file

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

View file

@ -19,6 +19,7 @@ use super::PredicateObligation;
use super::SelectionContext;
use super::SelectionError;
use super::VtableClosureData;
use super::VtableGeneratorData;
use super::VtableFnPointerData;
use super::VtableImplData;
use super::util;
@ -884,6 +885,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={:?}",
@ -1043,6 +1045,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) =>
@ -1125,6 +1129,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, suspend_ty, return_ty)) =
tcx.generator_trait_ref_and_outputs(gen_def_id,
obligation.predicate.trait_ref.self_ty(),
gen_sig);
let name = obligation.predicate.item_name(tcx);
let ty = if name == Symbol::intern("Return") {
return_ty
} else if name == Symbol::intern("Yield") {
suspend_ty
} else {
bug!()
};
let predicate = ty::Binder(ty::ProjectionPredicate { // (1) recreate binder here
projection_ty: ty::ProjectionTy {
trait_ref: trait_ref,
item_def_id: obligation.predicate.item_def_id,
},
ty: ty
});
confirm_param_env_candidate(selcx, obligation, predicate)
.with_addl_obligations(vtable.nested)
.with_addl_obligations(obligations)
}
fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,

View file

@ -24,9 +24,9 @@ use super::{ObjectCastObligation, Obligation};
use super::TraitNotObjectSafe;
use super::Selection;
use super::SelectionResult;
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure, VtableGenerator,
VtableFnPointer, VtableObject, VtableDefaultImpl};
use super::{VtableImplData, VtableObjectData, VtableBuiltinData,
use super::{VtableImplData, VtableObjectData, VtableBuiltinData, VtableGeneratorData,
VtableClosureData, VtableDefaultImplData, VtableFnPointerData};
use super::util;
@ -43,6 +43,7 @@ use middle::lang_items;
use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::snapshot_vec::{SnapshotVecDelegate, SnapshotVec};
use std::iter;
use std::cell::RefCell;
use std::cmp;
use std::fmt;
@ -197,6 +198,10 @@ enum SelectionCandidate<'tcx> {
/// confirmation step what ClosureKind obligation to emit.
ClosureCandidate(/* closure */ DefId, ty::ClosureSubsts<'tcx>, ty::ClosureKind),
/// Implementation of a `Generator` trait by one of the anonymous types
/// generated for a generator.
GeneratorCandidate(/* function / closure */ DefId, ty::ClosureSubsts<'tcx>),
/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int)->int`)
FnPointerCandidate,
@ -228,6 +233,11 @@ impl<'a, 'tcx> ty::Lift<'tcx> for SelectionCandidate<'a> {
ParamCandidate(ref trait_ref) => {
return tcx.lift(trait_ref).map(ParamCandidate);
}
GeneratorCandidate(def_id, ref substs) => {
return tcx.lift(substs).map(|substs| {
GeneratorCandidate(def_id, substs)
});
}
ClosureCandidate(def_id, ref substs, kind) => {
return tcx.lift(substs).map(|substs| {
ClosureCandidate(def_id, substs, kind)
@ -1295,6 +1305,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
&mut candidates)?;
} 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.gen_trait() == Some(def_id) {
self.assemble_generator_candidates(obligation, &mut candidates)?;
} else {
self.assemble_closure_candidates(obligation, &mut candidates)?;
self.assemble_fn_pointer_candidates(obligation, &mut candidates)?;
@ -1480,6 +1492,31 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
})
}
fn assemble_generator_candidates(&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>)
-> Result<(),SelectionError<'tcx>>
{
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.
///
@ -1848,6 +1885,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}
ImplCandidate(..) |
ClosureCandidate(..) |
GeneratorCandidate(..) |
FnPointerCandidate |
BuiltinObjectCandidate |
BuiltinUnsizeCandidate |
@ -1928,7 +1966,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
@ -1980,7 +2018,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
}
@ -2081,6 +2119,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()
@ -2190,6 +2233,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
@ -2518,6 +2567,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,
@ -3019,6 +3102,45 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
&trait_ref)
}
fn generator_trait_ref_unnormalized(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: DefId,
substs: ty::ClosureSubsts<'tcx>)
-> ty::PolyTraitRef<'tcx>
{
let gen_sig = self.infcx.generator_sig(closure_def_id).unwrap()
.subst(self.tcx(), substs.substs);
let ty::Binder((trait_ref, ..)) =
self.tcx().generator_trait_ref_and_outputs(obligation.predicate.def_id(),
obligation.predicate.0.self_ty(), // (1)
gen_sig);
// (1) Feels icky to skip the binder here, but OTOH we know
// that the self-type is an generator type and hence is
// in fact unparameterized (or at least does not reference any
// regions bound in the obligation). Still probably some
// refactoring could make this nicer.
ty::Binder(trait_ref)
}
fn generator_trait_ref(&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: DefId,
substs: ty::ClosureSubsts<'tcx>)
-> Normalized<'tcx, ty::PolyTraitRef<'tcx>>
{
let trait_ref = self.generator_trait_ref_unnormalized(
obligation, closure_def_id, substs);
// A generator signature can contain associated types which
// must be normalized.
normalize_with_depth(self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth+1,
&trait_ref)
}
/// Returns the obligations that are implied by instantiating an
/// impl or trait. The obligations are substituted and fully
/// normalized. This is used when confirming an impl or default

View file

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

View file

@ -513,6 +513,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
ty::Binder((trait_ref, sig.skip_binder().output()))
}
pub fn generator_trait_ref_and_outputs(self,
fn_trait_def_id: DefId,
self_ty: Ty<'tcx>,
sig: ty::PolyGenSig<'tcx>)
-> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)>
{
let trait_ref = ty::TraitRef {
def_id: fn_trait_def_id,
substs: self.mk_substs_trait(self_ty, &[sig.skip_binder().impl_arg_ty]),
};
ty::Binder((trait_ref, sig.skip_binder().suspend_ty, sig.skip_binder().return_ty))
}
pub fn impl_is_default(self, node_item_def_id: DefId) -> bool {
match self.hir.as_local_node_id(node_item_def_id) {
Some(node_id) => {

View file

@ -30,7 +30,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;
@ -233,6 +233,10 @@ pub struct TypeckTables<'tcx> {
/// that caused the closure to be this kind.
pub closure_kinds: NodeMap<(ty::ClosureKind, Option<(Span, ast::Name)>)>,
pub generator_sigs: NodeMap<Option<ty::GenSig<'tcx>>>,
pub generator_interiors: NodeMap<ty::GeneratorInterior<'tcx>>,
/// For each fn, records the "liberated" types of its arguments
/// and return type. Liberated means that all bound regions
/// (including late-bound regions) are replaced with free
@ -275,6 +279,8 @@ impl<'tcx> TypeckTables<'tcx> {
node_substs: NodeMap(),
adjustments: NodeMap(),
upvar_capture_map: FxHashMap(),
generator_sigs: NodeMap(),
generator_interiors: NodeMap(),
closure_tys: NodeMap(),
closure_kinds: NodeMap(),
liberated_fn_sigs: NodeMap(),
@ -1040,7 +1046,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());
@ -1395,6 +1401,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.mk_ty(TyClosure(closure_id, closure_substs))
}
pub fn mk_generator(self,
id: DefId,
closure_substs: ClosureSubsts<'tcx>,
interior: GeneratorInterior<'tcx>)
-> Ty<'tcx> {
self.mk_ty(TyGenerator(id, closure_substs, interior))
}
pub fn mk_var(self, v: TyVid) -> Ty<'tcx> {
self.mk_infer(TyVar(v))
}

View file

@ -213,6 +213,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
|p| format!("trait {}", tcx.item_path_str(p.def_id())))
}
ty::TyClosure(..) => "closure".to_string(),
ty::TyGenerator(..) => "generator".to_string(),
ty::TyTuple(..) => "tuple".to_string(),
ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(),
ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(),

View file

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

View file

@ -85,6 +85,15 @@ impl FlagComputation {
}
}
&ty::TyGenerator(_, ref substs, ref interior) => {
// FIXME: Find out why TyClosure has HAS_TY_CLOSURE
// and see if the same reason applies here
self.add_flags(TypeFlags::HAS_TY_CLOSURE);
self.add_flags(TypeFlags::HAS_LOCAL_NAMES);
self.add_substs(&substs.substs);
self.add_ty(interior.witness());
}
&ty::TyClosure(_, ref substs) => {
self.add_flags(TypeFlags::HAS_TY_CLOSURE);
self.add_flags(TypeFlags::HAS_LOCAL_NAMES);

View file

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

View file

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

View file

@ -540,8 +540,13 @@ 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
}
}
}
@ -909,6 +914,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: TypeckTables(DefId) -> Option<ty::PolyGenSig<'tcx>>,
/// Caches CoerceUnsized kinds for impls on custom types.
[] coerce_unsized_info: CoerceUnsizedInfo(DefId)
-> ty::adjustment::CoerceUnsizedInfo,

View file

@ -25,6 +25,7 @@ use middle::privacy::AccessLevels;
use middle::resolve_lifetime::ObjectLifetimeDefault;
use middle::region::CodeExtent;
use mir::Mir;
use mir::GeneratorLayout;
use traits;
use ty;
use ty::subst::{Subst, Substs};
@ -59,9 +60,9 @@ use rustc_data_structures::transitive_relation::TransitiveRelation;
use hir;
pub use self::sty::{Binder, DebruijnIndex};
pub use self::sty::{FnSig, PolyFnSig};
pub use self::sty::{FnSig, GenSig, PolyFnSig, PolyGenSig};
pub use self::sty::{InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
pub use self::sty::{ClosureSubsts, TypeAndMut};
pub use self::sty::{ClosureSubsts, GeneratorInterior, TypeAndMut};
pub use self::sty::{TraitRef, TypeVariants, PolyTraitRef};
pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef};
pub use self::sty::{ExistentialProjection, PolyExistentialProjection};
@ -1699,7 +1700,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![]
}
@ -1976,6 +1977,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
},
}
},
Some(hir_map::NodeImplArg(_)) => Symbol::intern("impl arg").as_str(),
r => bug!("Variable id {} maps to {:?}, not local", id, r),
}
}
@ -1996,6 +1998,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
hir::ExprUnary(hir::UnDeref, _) |
hir::ExprField(..) |
hir::ExprTupField(..) |
hir::ExprImplArg(_) |
hir::ExprIndex(..) => {
true
}
@ -2027,6 +2030,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
hir::ExprBox(..) |
hir::ExprAddrOf(..) |
hir::ExprBinary(..) |
hir::ExprSuspend(..) |
hir::ExprCast(..) => {
false
}
@ -2258,6 +2262,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.trait_def(trait_def_id).has_default_impl
}
pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> {
self.optimized_mir(def_id).generator_layout.as_ref().unwrap()
}
/// Given the def_id of an impl, return the def_id of the trait it implements.
/// If it implements no trait, return `None`.
pub fn trait_id_of_impl(self, def_id: DefId) -> Option<DefId> {

View file

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

View file

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

View file

@ -29,6 +29,15 @@ impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) {
}
}
impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) {
type Lifted = (A::Lifted, B::Lifted, C::Lifted);
fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
tcx.lift(&self.0).and_then(|a| {
tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))
})
}
}
impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
type Lifted = Option<T::Lifted>;
fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@ -220,6 +229,15 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> {
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorInterior<'a> {
type Lifted = ty::GeneratorInterior<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
tcx.lift(&self.witness()).map(|witness| {
ty::GeneratorInterior(witness)
})
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> {
type Lifted = ty::adjustment::Adjustment<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@ -283,6 +301,19 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> {
}
}
impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> {
type Lifted = ty::GenSig<'tcx>;
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
tcx.lift(&(self.impl_arg_ty, self.suspend_ty, self.return_ty)).map(|(impl_arg_ty, suspend_ty, return_ty)| {
ty::GenSig {
impl_arg_ty,
suspend_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> {
@ -541,6 +572,7 @@ 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)),
@ -572,6 +604,7 @@ 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),
@ -596,6 +629,22 @@ 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 {
impl_arg_ty: self.impl_arg_ty.fold_with(folder),
suspend_ty: self.suspend_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.impl_arg_ty.visit_with(visitor) ||
self.suspend_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);
@ -686,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(self.0.fold_with(folder))
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.0.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::adjustment::Adjustment<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::adjustment::Adjustment {

View file

@ -149,6 +149,9 @@ pub enum TypeVariants<'tcx> {
/// `|a| a`.
TyClosure(DefId, ClosureSubsts<'tcx>),
/// The anonymous type of a generator. Pairs with a TyClosure for closure generators.
TyGenerator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>),
/// The never type `!`
TyNever,
@ -275,6 +278,50 @@ impl<'a, 'gcx, 'acx, 'tcx> ClosureSubsts<'tcx> {
}
}
impl<'a, 'gcx, 'tcx> ClosureSubsts<'tcx> {
pub fn state_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'tcx
{
let state = tcx.generator_layout(def_id).fields.iter();
let state: Vec<_> = state.map(|d| d.ty.subst(tcx, self.substs)).collect();
state.into_iter()
}
pub fn field_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) ->
impl Iterator<Item=Ty<'tcx>> + 'tcx
{
let upvars = self.upvar_tys(def_id, tcx);
let state = self.state_tys(def_id, tcx);
let tys: Vec<_> = upvars.chain(iter::once(tcx.types.u32)).chain(state).collect();
tys.into_iter()
}
}
/// 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 Ty<'tcx>);
impl<'tcx> GeneratorInterior<'tcx> {
pub fn new(witness: Ty<'tcx>) -> GeneratorInterior<'tcx> {
GeneratorInterior(witness)
}
pub fn witness(&self) -> Ty<'tcx> {
self.0
}
pub fn as_slice(&self) -> &'tcx Slice<Ty<'tcx>> {
match self.0.sty {
ty::TyTuple(s, _) => s,
_ => bug!(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub enum ExistentialPredicate<'tcx> {
/// e.g. Iterator
@ -593,6 +640,25 @@ impl<'a, 'tcx> ProjectionTy<'tcx> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct GenSig<'tcx> {
pub impl_arg_ty: Ty<'tcx>,
pub suspend_ty: Ty<'tcx>,
pub return_ty: Ty<'tcx>,
}
#[allow(warnings)]
pub type PolyGenSig<'tcx> = Binder<GenSig<'tcx>>;
#[allow(warnings)]
impl<'tcx> PolyGenSig<'tcx> {
pub fn suspend_ty(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|sig| sig.suspend_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.
@ -1393,7 +1459,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
TyAdt(_, substs) | TyAnon(_, substs) => {
substs.regions().collect()
}
TyClosure(_, ref substs) => {
TyClosure(_, ref substs) | TyGenerator(_, ref substs, _) => {
substs.substs.regions().collect()
}
TyProjection(ref data) => {

View file

@ -27,6 +27,7 @@ use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
HashStable};
use rustc_data_structures::fx::FxHashMap;
use std::cmp;
use std::iter;
use std::hash::Hash;
use std::intrinsics;
use syntax::ast::{self, Name};
@ -568,6 +569,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
@ -689,6 +696,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),
@ -1111,6 +1119,8 @@ fn needs_drop_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty::TyClosure(def_id, ref substs) => substs.upvar_tys(def_id, tcx).any(needs_drop),
ty::TyGenerator(..) => true,
ty::TyTuple(ref tys, _) => tys.iter().cloned().any(needs_drop),
// unions don't have destructors regardless of the child types

View file

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

View file

@ -239,8 +239,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
}
}
ty::TyClosure(..) => {
// the types in a closure are always the types of
ty::TyGenerator(..) | ty::TyClosure(..) => {
// the types in a closure or generator are always the types of
// local variables (or possibly references to local
// variables), we'll walk those.
//

View file

@ -17,7 +17,7 @@ use ty::{BrAnon, BrEnv, BrFresh, BrNamed};
use ty::{TyBool, TyChar, TyAdt};
use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr};
use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple};
use ty::{TyClosure, TyProjection, TyAnon};
use ty::{TyClosure, TyGenerator, TyProjection, TyAnon};
use ty::{TyDynamic, TyInt, TyUint, TyInfer};
use ty::{self, Ty, TyCtxt, TypeFoldable};
@ -715,6 +715,12 @@ impl<'tcx> fmt::Display for ty::TraitRef<'tcx> {
}
}
impl<'tcx> fmt::Display for ty::GeneratorInterior<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@ -813,6 +819,41 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
})
}
TyStr => write!(f, "str"),
TyGenerator(did, substs, interior) => ty::tls::with(|tcx| {
let upvar_tys = substs.upvar_tys(did, tcx);
write!(f, "[generator")?;
if let Some(node_id) = tcx.hir.as_local_node_id(did) {
write!(f, "@{:?}", tcx.hir.span(node_id))?;
let mut sep = " ";
tcx.with_freevars(node_id, |freevars| {
for (freevar, upvar_ty) in freevars.iter().zip(upvar_tys) {
let def_id = freevar.def.def_id();
let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
write!(f,
"{}{}:{}",
sep,
tcx.local_var_name_str(node_id),
upvar_ty)?;
sep = ", ";
}
Ok(())
})?
} else {
// cross-crate closure types should only be
// visible in trans bug reports, I imagine.
write!(f, "@{:?}", did)?;
let mut sep = " ";
for (index, upvar_ty) in upvar_tys.enumerate() {
write!(f, "{}{}:{}", sep, index, upvar_ty)?;
sep = ", ";
}
}
write!(f, " {}", interior)?;
write!(f, "]")
}),
TyClosure(did, substs) => ty::tls::with(|tcx| {
let upvar_tys = substs.upvar_tys(did, tcx);
write!(f, "[closure")?;

View file

@ -21,6 +21,7 @@ use borrowck::move_data::MoveData;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::region::extent_has_yield;
use rustc::middle::region;
use rustc::ty::{self, TyCtxt};
@ -36,19 +37,20 @@ mod gather_moves;
mod move_error;
pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
body: hir::BodyId)
body_id: hir::BodyId)
-> (Vec<Loan<'tcx>>, move_data::MoveData<'tcx>) {
let def_id = bccx.tcx.hir.body_owner_def_id(body);
let def_id = bccx.tcx.hir.body_owner_def_id(body_id);
let param_env = bccx.tcx.param_env(def_id);
let body = bccx.tcx.hir.body(body_id);
let mut glcx = GatherLoanCtxt {
bccx: bccx,
all_loans: Vec::new(),
item_ub: region::CodeExtent::Misc(body.node_id),
item_ub: region::CodeExtent::Misc(body_id.node_id),
move_data: MoveData::new(),
move_error_collector: move_error::MoveErrorCollector::new(),
generator: body.is_generator(),
};
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);
@ -65,6 +67,7 @@ struct GatherLoanCtxt<'a, 'tcx: 'a> {
/// `item_ub` is used as an upper-bound on the lifetime whenever we
/// ask for the scope of an expression categorized as an upvar.
item_ub: region::CodeExtent,
generator: bool,
}
impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
@ -134,6 +137,21 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for GatherLoanCtxt<'a, 'tcx> {
borrow_id, cmt, loan_region,
bk, loan_cause);
let borrows_impl_arg = match cmt.cat {
Categorization::Local(id) => match self.bccx.tcx.hir.find(id) {
Some(hir::map::NodeImplArg(..)) => true,
_ => false,
},
_ => false,
};
if borrows_impl_arg {
span_err!(self.bccx.tcx.sess,
borrow_span,
E0805,
"cannot borrow the implicit argument of a generator");
}
self.guarantee_valid(borrow_id,
borrow_span,
cmt,
@ -201,6 +219,19 @@ fn check_aliasability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
}
}
fn check_yields<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
borrow_span: Span,
loan_region: ty::Region<'tcx>) {
if let &ty::RegionKind::ReScope(extent) = loan_region {
if extent_has_yield(bccx.tcx, extent) {
span_err!(bccx.tcx.sess,
borrow_span,
E0806,
"cannot borrow this value across the suspend point of a generator");
}
}
}
/// Implements the M-* rules in README.md.
fn check_mutability<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
borrow_span: Span,
@ -312,6 +343,11 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
return;
}
// Check that the region has no yields if this is in a generator
if self.generator {
check_yields(self.bccx, borrow_span, loan_region);
}
// Check that the lifetime of the borrow does not exceed
// the lifetime of the data being borrowed.
if lifetime::guarantee_lifetime(self.bccx, self.item_ub,

View file

@ -306,7 +306,7 @@ pub fn closure_to_block(closure_id: ast::NodeId,
tcx: TyCtxt) -> ast::NodeId {
match tcx.hir.get(closure_id) {
hir_map::NodeExpr(expr) => match expr.node {
hir::ExprClosure(.., body_id, _) => {
hir::ExprClosure(.., body_id, _, _) => {
body_id.node_id
}
_ => {
@ -609,7 +609,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)"),

View file

@ -1191,4 +1191,6 @@ register_diagnostics! {
E0594, // cannot assign to {}
E0595, // closure cannot assign to {}
E0598, // lifetime of {} is too short to guarantee its contents can be...
E0805, // borrow of the implicit argument of a generator
E0806, // borrow across a suspend point
}

View file

@ -21,6 +21,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 +110,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())

View file

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

View file

@ -292,6 +292,15 @@ impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> {
}
}
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Box<T> {
#[inline]
fn hash_stable<W: StableHasherResult>(&self,
ctx: &mut CTX,
hasher: &mut StableHasher<W>) {
(**self).hash_stable(ctx, hasher);
}
}
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ::std::rc::Rc<T> {
#[inline]
fn hash_stable<W: StableHasherResult>(&self,

View file

@ -957,6 +957,10 @@ 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::no_landing_pads::NoLandingPads);
passes.push_pass(MIR_OPTIMIZED, mir::transform::add_call_guards::AddCallGuards);
passes.push_pass(MIR_OPTIMIZED, mir::transform::dump_mir::Marker("PreTrans"));

View file

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

View file

@ -104,6 +104,7 @@ provide! { <'tcx> tcx, def_id, cdata,
mir
}
generator_sig => { cdata.generator_sig(def_id.index, tcx) }
mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
typeck_tables_of => { cdata.item_body_tables(def_id.index, tcx) }
closure_kind => { cdata.closure_kind(def_id.index) }

View file

@ -438,6 +438,7 @@ impl<'tcx> EntryKind<'tcx> {
EntryKind::Impl(_) |
EntryKind::DefaultImpl(_) |
EntryKind::Field |
EntryKind::Generator(_) |
EntryKind::Closure(_) => return None,
})
}
@ -1100,6 +1101,23 @@ impl<'a, 'tcx> CrateMetadata {
sig.decode((self, tcx))
}
fn get_generator_data(&self,
id: DefIndex,
tcx: TyCtxt<'a, 'tcx, 'tcx>)
-> Option<GeneratorData<'tcx>> {
match self.entry(id).kind {
EntryKind::Generator(data) => Some(data.decode((self, tcx))),
_ => None,
}
}
pub fn generator_sig(&self,
id: DefIndex,
tcx: TyCtxt<'a, 'tcx, 'tcx>)
-> Option<ty::PolyGenSig<'tcx>> {
self.get_generator_data(id, tcx).map(|d| d.sig)
}
#[inline]
pub fn def_key(&self, index: DefIndex) -> DefKey {
self.def_path_table.def_key(index)

View file

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

View file

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

View file

@ -80,6 +80,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
success.and(slice.index(idx))
}
ExprKind::SelfRef => {
block.and(Lvalue::Local(Local::new(this.arg_offset + 1)))
}
ExprKind::ImplArg => {
block.and(Lvalue::Local(Local::new(1)))
}
ExprKind::VarRef { id } => {
@ -118,6 +121,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ExprKind::Return { .. } |
ExprKind::Literal { .. } |
ExprKind::InlineAsm { .. } |
ExprKind::Suspend { .. } |
ExprKind::Call { .. } => {
// these are not lvalues, so we need to make a temporary.
debug_assert!(match Category::of(&expr.kind) {

View file

@ -179,12 +179,25 @@ 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, generator } => { // 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 generator {
// Add the state operand
operands.push(Operand::Constant(box Constant {
span: expr_span,
ty: this.hir.tcx().types.u32,
literal: Literal::Value {
value: ConstVal::Integral(ConstInt::U32(0)),
},
}));
box AggregateKind::Generator(closure_id, substs)
} else {
box AggregateKind::Closure(closure_id, substs)
};
block.and(Rvalue::Aggregate(result, operands))
}
ExprKind::Adt {
adt_def, variant_index, substs, fields, base
@ -226,6 +239,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block = unpack!(this.stmt_expr(block, expr));
block.and(this.unit_rvalue())
}
ExprKind::Suspend { value } => {
let value = unpack!(block = this.as_operand(block, scope, value));
let impl_arg_ty = this.impl_arg_ty.unwrap();
block = unpack!(this.build_drop(block,
expr_span,
Lvalue::Local(Local::new(1)),
impl_arg_ty));
let resume = this.cfg.start_new_block();
let cleanup = this.generator_drop_cleanup(expr_span);
this.cfg.terminate(block, source_info, TerminatorKind::Suspend {
value: value,
resume: resume,
drop: cleanup,
});
resume.and(this.unit_rvalue())
}
ExprKind::Literal { .. } |
ExprKind::Block { .. } |
ExprKind::Match { .. } |
@ -243,6 +272,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ExprKind::Continue { .. } |
ExprKind::Return { .. } |
ExprKind::InlineAsm { .. } |
ExprKind::ImplArg |
ExprKind::StaticRef { .. } => {
// these do not have corresponding `Rvalue` variants,
// so make an operand and then return that

View file

@ -79,7 +79,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// anything because no values with a destructor can be created in
// a constant at this time, even if the type may need dropping.
if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop(expr_span, temp_lifetime, &temp, expr_ty);
this.schedule_drop(expr_span, temp_lifetime, &temp, expr_ty, false);
}
block.and(temp)

View file

@ -50,6 +50,7 @@ impl Category {
ExprKind::Index { .. } |
ExprKind::SelfRef |
ExprKind::VarRef { .. } |
ExprKind::ImplArg |
ExprKind::StaticRef { .. } =>
Some(Category::Lvalue),
@ -77,6 +78,7 @@ impl Category {
ExprKind::Borrow { .. } |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::Suspend { .. } |
ExprKind::InlineAsm { .. } =>
Some(Category::Rvalue(RvalueFunc::AsRvalue)),

View file

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

View file

@ -203,7 +203,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let local_id = self.var_indices[&var];
let var_ty = self.local_decls[local_id].ty;
let extent = self.hir.region_maps.var_scope(var);
self.schedule_drop(span, extent, &Lvalue::Local(local_id), var_ty);
self.schedule_drop(span, extent, &Lvalue::Local(local_id), var_ty, false);
}
pub fn visit_bindings<F>(&mut self, pattern: &Pattern<'tcx>, mut f: &mut F)

View file

@ -71,7 +71,7 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
// Assume that everything other than closures
// is a constant "initializer" expression.
match expr.node {
hir::ExprClosure(_, _, body, _) => body,
hir::ExprClosure(_, _, body, _, _) => body,
_ => hir::BodyId { node_id: expr.id }
}
}
@ -94,13 +94,18 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
let ty = tcx.type_of(tcx.hir.local_def_id(id));
let mut abi = fn_sig.abi;
let implicit_argument = if let ty::TyClosure(..) = ty.sty {
// HACK(eddyb) Avoid having RustCall on closures,
// as it adds unnecessary (and wrong) auto-tupling.
abi = Abi::Rust;
Some((closure_self_ty(tcx, id, body_id), None))
} else {
None
let implicit_argument = match ty.sty {
ty::TyClosure(..) => {
// HACK(eddyb) Avoid having RustCall on closures,
// as it adds unnecessary (and wrong) auto-tupling.
abi = Abi::Rust;
Some((closure_self_ty(tcx, id, body_id), None))
}
ty::TyGenerator(..) => {
let gen_ty = tcx.body_tables(body_id).node_id_to_type(id);
Some((gen_ty, None))
}
_ => None,
};
let body = tcx.hir.body(body_id);
@ -113,7 +118,15 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
});
let arguments = implicit_argument.into_iter().chain(explicit_arguments);
build::construct_fn(cx, id, arguments, abi, fn_sig.output(), body)
let (suspend_ty, impl_arg_ty, return_ty) = if body.is_generator() {
let gen_sig = cx.tables().generator_sigs[&id].clone().unwrap();
(Some(gen_sig.suspend_ty), Some(gen_sig.impl_arg_ty), gen_sig.return_ty)
} else {
(None, None, fn_sig.output())
};
build::construct_fn(cx, id, arguments, abi, return_ty, suspend_ty, impl_arg_ty, body)
} else {
build::construct_const(cx, body_id)
};
@ -198,7 +211,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
///////////////////////////////////////////////////////////////////////////
// BuildMir -- walks a crate, looking for fn items and methods to build MIR from
fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pub fn closure_self_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
closure_expr_id: ast::NodeId,
body_id: hir::BodyId)
-> Ty<'tcx> {
@ -231,6 +244,9 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
fn_span: Span,
arg_count: usize,
arg_offset: usize,
impl_arg_ty: Option<Ty<'tcx>>,
/// the current set of scopes, updated as we traverse;
/// see the `scope` module for more details
@ -326,6 +342,8 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
arguments: A,
abi: Abi,
return_ty: Ty<'gcx>,
suspend_ty: Option<Ty<'gcx>>,
impl_arg_ty: Option<Ty<'gcx>>,
body: &'gcx hir::Body)
-> Mir<'tcx>
where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
@ -334,7 +352,13 @@ 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 arg_offset = if impl_arg_ty.is_some() { 1 } else { 0 };
let mut builder = Builder::new(hir.clone(),
span,
arguments.len() + arg_offset,
arg_offset,
impl_arg_ty,
return_ty);
let call_site_extent = CodeExtent::CallSiteScope(body.id());
let arg_extent = CodeExtent::ParameterScope(body.id());
@ -342,7 +366,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
let source_info = builder.source_info(span);
unpack!(block = builder.in_scope((call_site_extent, source_info), block, |builder| {
unpack!(block = builder.in_scope((arg_extent, source_info), block, |builder| {
builder.args_and_body(block, &arguments, arg_extent, &body.value)
builder.args_and_body(block, &arguments, arg_extent, impl_arg_ty, &body.value)
}));
// Attribute epilogue to function's closing brace
let fn_end = Span { lo: span.hi, ..span };
@ -387,7 +411,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, suspend_ty);
mir.spread_arg = spread_arg;
mir
}
@ -400,7 +424,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
let ty = hir.tables().expr_ty_adjusted(ast_expr);
let owner_id = tcx.hir.body_owner(body_id);
let span = tcx.hir.span(owner_id);
let mut builder = Builder::new(hir.clone(), span, 0, ty);
let mut builder = Builder::new(hir.clone(), span, 0, 0, None, ty);
let mut block = START_BLOCK;
let expr = builder.hir.mirror(ast_expr);
@ -412,7 +436,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>,
@ -420,16 +444,18 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
-> Mir<'tcx> {
let span = hir.tcx().hir.span(hir.tcx().hir.body_owner(body_id));
let ty = hir.tcx().types.err;
let mut builder = Builder::new(hir, span, 0, ty);
let mut builder = Builder::new(hir, span, 0, 0, None, ty);
let source_info = builder.source_info(span);
builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
builder.finish(vec![], ty)
builder.finish(vec![], ty, None)
}
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn new(hir: Cx<'a, 'gcx, 'tcx>,
span: Span,
arg_count: usize,
arg_offset: usize,
impl_arg_ty: Option<Ty<'tcx>>,
return_ty: Ty<'tcx>)
-> Builder<'a, 'gcx, 'tcx> {
let mut builder = Builder {
@ -437,6 +463,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
cfg: CFG { basic_blocks: IndexVec::new() },
fn_span: span,
arg_count: arg_count,
arg_offset,
impl_arg_ty,
scopes: vec![],
visibility_scopes: IndexVec::new(),
visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
@ -458,7 +486,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn finish(self,
upvar_decls: Vec<UpvarDecl>,
return_ty: Ty<'tcx>)
return_ty: Ty<'tcx>,
suspend_ty: Option<Ty<'tcx>>)
-> Mir<'tcx> {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
@ -470,6 +499,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
self.visibility_scopes,
IndexVec::new(),
return_ty,
suspend_ty,
self.local_decls,
self.arg_count,
upvar_decls,
@ -481,9 +511,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
mut block: BasicBlock,
arguments: &[(Ty<'gcx>, Option<&'gcx hir::Pat>)],
argument_extent: CodeExtent,
impl_arg_ty: Option<Ty<'gcx>>,
ast_body: &'gcx hir::Expr)
-> BlockAnd<()>
{
if let Some(impl_arg_ty) = impl_arg_ty {
self.local_decls.push(LocalDecl {
mutability: Mutability::Mut,
ty: impl_arg_ty,
is_user_variable: false,
source_info: SourceInfo {
scope: ARGUMENT_VISIBILITY_SCOPE,
span: self.fn_span,
},
name: None,
});
let lvalue = Lvalue::Local(Local::new(1));
// Make sure we drop the argument on completion
self.schedule_drop(ast_body.span, argument_extent, &lvalue, impl_arg_ty, true);
};
// Allocate locals for the function arguments
for &(ty, pattern) in arguments.iter() {
// If this is a simple binding pattern, give the local a nice name for debuginfo.
@ -510,7 +557,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Bind the argument patterns
for (index, &(ty, pattern)) in arguments.iter().enumerate() {
// Function arguments always get the first Local indices after the return pointer
let lvalue = Lvalue::Local(Local::new(index + 1));
let lvalue = Lvalue::Local(Local::new(self.arg_offset + index + 1));
if let Some(pattern) = pattern {
let pattern = Pattern::from_hir(self.hir.tcx().global_tcx(),
@ -523,7 +570,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// Make sure we drop (parts of) the argument even when not matched on.
self.schedule_drop(pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
argument_extent, &lvalue, ty);
argument_extent, &lvalue, ty, false);
}

View file

@ -124,6 +124,9 @@ pub struct Scope<'tcx> {
/// end of the vector (top of the stack) first.
drops: Vec<DropData<'tcx>>,
/// Is the first drop the drop of the implicit argument?
impl_arg_drop: bool,
/// A scope may only have one associated free, because:
///
/// 1. We require a `free` to only be scheduled in the scope of
@ -141,6 +144,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)]
@ -155,14 +161,22 @@ struct DropData<'tcx> {
kind: DropKind
}
#[derive(Debug, Default, Clone, Copy)]
struct CachedBlock {
/// The cached block for the cleanups-on-diverge path. This block
/// contains code to run the current drop and all the preceding
/// drops (i.e. those having lower index in Drops Scope drop
/// array)
unwind: Option<BasicBlock>,
/// The cached block for unwinds during cleanups-on-generator-drop path
generator_drop: Option<BasicBlock>,
}
#[derive(Debug)]
enum DropKind {
Value {
/// The cached block for the cleanups-on-diverge path. This block
/// contains code to run the current drop and all the preceding
/// drops (i.e. those having lower index in Drops Scope drop
/// array)
cached_block: Option<BasicBlock>
cached_block: CachedBlock,
},
Storage
}
@ -180,7 +194,7 @@ struct FreeData<'tcx> {
/// The cached block containing code to run the free. The block will also execute all the drops
/// in the scope.
cached_block: Option<BasicBlock>
cached_block: CachedBlock,
}
#[derive(Clone, Debug)]
@ -197,6 +211,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<'tcx> Scope<'tcx> {
/// Invalidate all the cached blocks in the scope.
///
@ -209,11 +246,27 @@ 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();
}
}
if let Some(ref mut freedata) = self.free {
freedata.cached_block = None;
freedata.cached_block.invalidate();
}
}
fn drops(&self, generator_drop: bool) -> &[DropData<'tcx>] {
if self.impl_arg_drop && generator_drop {
&self.drops[1..]
} else {
&self.drops[..]
}
}
fn drops_mut(&mut self, generator_drop: bool) -> &mut [DropData<'tcx>] {
if self.impl_arg_drop && generator_drop {
&mut self.drops[1..]
} else {
&mut self.drops[..]
}
}
@ -221,17 +274,19 @@ 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> {
let mut drops = self.drops.iter().rev().filter_map(|data| {
fn cached_block(&self, generator_drop: bool) -> Option<BasicBlock> {
let mut drops = self.drops(generator_drop).iter().rev().filter_map(|data| {
match data.kind {
DropKind::Value { cached_block } => Some(cached_block),
DropKind::Value { cached_block } => {
Some(cached_block.get(generator_drop))
}
DropKind::Storage => None
}
});
if let Some(cached_block) = drops.next() {
Some(cached_block.expect("drop cache is not filled"))
} else if let Some(ref data) = self.free {
Some(data.cached_block.expect("free cache is not filled"))
Some(data.cached_block.get(generator_drop).expect("free cache is not filled"))
} else {
None
}
@ -320,7 +375,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
extent: extent,
needs_cleanup: false,
drops: vec![],
impl_arg_drop: false,
free: None,
cached_generator_drop: None,
cached_exits: FxHashMap()
});
}
@ -342,7 +399,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()
@ -385,7 +443,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);
@ -393,7 +452,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let Some(ref free_data) = scope.free {
let next = self.cfg.start_new_block();
let free = build_free(self.hir.tcx(), &tmp, free_data, next);
self.cfg.terminate(block, scope.source_info(span), free);
self.cfg.terminate(block, scope.source_info(free_data.span), free);
block = next;
}
}
@ -403,6 +462,63 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
TerminatorKind::Goto { target: target });
}
/// Creates a path that performs all required cleanup for dropping a generator.
///
/// This path terminates in GeneratorDrop. Returns the start of the path.
/// None indicates theres no cleanup to do at this point.
pub fn generator_drop_cleanup(&mut self, span: Span) -> Option<BasicBlock> {
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
return None;
}
// Fill in the cache
self.diverge_cleanup_gen(span, true);
let src_info = self.scopes[0].source_info(self.fn_span);
let tmp = self.get_unit_temp();
let mut block = self.cfg.start_new_block();
let result = block;
let mut rest = &mut self.scopes[..];
while let Some((scope, rest_)) = {rest}.split_last_mut() {
rest = rest_;
if !scope.needs_cleanup {
continue;
}
block = if let Some(b) = scope.cached_generator_drop {
self.cfg.terminate(block, src_info,
TerminatorKind::Goto { target: b });
return Some(result);
} else {
let b = self.cfg.start_new_block();
scope.cached_generator_drop = Some(b);
self.cfg.terminate(block, src_info,
TerminatorKind::Goto { target: b });
b
};
unpack!(block = build_scope_drops(&mut self.cfg,
scope,
rest,
block,
self.arg_count,
true));
// End all regions for scopes out of which we are breaking.
self.cfg.push_end_region(block, src_info, scope.extent);
if let Some(ref free_data) = scope.free {
let next = self.cfg.start_new_block();
let free = build_free(self.hir.tcx(), &tmp, free_data, next);
self.cfg.terminate(block, scope.source_info(free_data.span), free);
block = next;
}
}
self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop);
Some(result)
}
/// Creates a new visibility scope, nested in the current one.
pub fn new_visibility_scope(&mut self, span: Span) -> VisibilityScope {
let parent = self.visibility_scope;
@ -487,7 +603,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
None,
MirSource::Fn(_) =>
Some(self.topmost_scope()),
MirSource::Promoted(..) =>
MirSource::Promoted(..) |
MirSource::GeneratorDrop(..) =>
bug!(),
}
}
@ -500,10 +617,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
span: Span,
extent: CodeExtent,
lvalue: &Lvalue<'tcx>,
lvalue_ty: Ty<'tcx>) {
lvalue_ty: Ty<'tcx>,
impl_arg: bool) {
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 {
@ -570,6 +688,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let extent_span = extent.span(&tcx.hir).unwrap();
// Attribute scope exit drops to scope's closing brace
let scope_end = Span { lo: extent_span.hi, .. extent_span};
if impl_arg {
assert!(scope.drops.is_empty());
scope.impl_arg_drop = true;
}
scope.drops.push(DropData {
span: scope_end,
location: lvalue.clone(),
@ -603,7 +725,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
span: span,
value: value.clone(),
item_ty: item_ty,
cached_block: None
cached_block: CachedBlock::default(),
});
return;
}
@ -619,6 +741,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// See module comment for more details. None indicates theres no
/// cleanup to do at this point.
pub fn diverge_cleanup(&mut self, span: Span) -> Option<BasicBlock> {
self.diverge_cleanup_gen(span, false)
}
fn diverge_cleanup_gen(&mut self, span: Span, generator_drop: bool) -> Option<BasicBlock> {
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
return None;
}
@ -652,7 +778,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
};
for scope in scopes.iter_mut() {
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, span, scope, target);
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, span, scope, target, generator_drop);
}
Some(target)
}
@ -729,9 +855,11 @@ 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<()> {
let mut iter = scope.drops.iter().rev().peekable();
let mut iter = scope.drops(generator_drop).iter().rev().peekable();
while let Some(drop_data) = iter.next() {
let source_info = scope.source_info(drop_data.span);
if let DropKind::Value { .. } = drop_data.kind {
@ -739,14 +867,14 @@ 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 } => cached_block,
DropKind::Value { cached_block } => cached_block.get(generator_drop),
DropKind::Storage => None
}
}).next();
// If theres no `cached_block`s within current scope,
// we must look for one in the enclosing scope.
let on_diverge = on_diverge.or_else(||{
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
earlier_scopes.iter().rev().flat_map(|s| s.cached_block(generator_drop)).next()
});
let next = cfg.start_new_block();
cfg.terminate(block, source_info, TerminatorKind::Drop {
@ -759,6 +887,11 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
match drop_data.kind {
DropKind::Value { .. } |
DropKind::Storage => {
// We do not need to emit these for generator drops
if generator_drop {
continue
}
// Only temps and vars need their storage dead.
match drop_data.location {
Lvalue::Local(index) if index.index() > arg_count => {}
@ -780,7 +913,8 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
unit_temp: &Lvalue<'tcx>,
span: Span,
scope: &mut Scope<'tcx>,
mut target: BasicBlock)
mut target: BasicBlock,
generator_drop: bool)
-> BasicBlock
{
// Build up the drops in **reverse** order. The end result will
@ -794,7 +928,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// The code in this function reads from right to left. At each
// point, we check for cached blocks representing the
// remainder. If everything is cached, we'll just walk right to
// left reading the cached results but never created anything.
// left reading the cached results but never create anything.
let visibility_scope = scope.visibility_scope;
let source_info = |span| SourceInfo {
@ -804,13 +938,13 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// Next, build up any free.
if let Some(ref mut free_data) = scope.free {
target = if let Some(cached_block) = free_data.cached_block {
target = if let Some(cached_block) = free_data.cached_block.get(generator_drop) {
cached_block
} else {
let into = cfg.start_new_cleanup_block();
cfg.terminate(into, source_info(free_data.span),
build_free(tcx, unit_temp, free_data, target));
free_data.cached_block = Some(into);
*free_data.cached_block.ref_mut(generator_drop) = Some(into);
into
};
}
@ -818,7 +952,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// Next, build up the drops. Here we iterate the vector in
// *forward* order, so that we generate drops[0] first (right to
// left in diagram above).
for (j, drop_data) in scope.drops.iter_mut().enumerate() {
for (j, drop_data) in scope.drops_mut(generator_drop).iter_mut().enumerate() {
debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data);
// Only full value drops are emitted in the diverging path,
// not StorageDead.
@ -829,7 +963,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// match the behavior of clang, but on inspection eddyb says
// this is not what clang does.
let cached_block = match drop_data.kind {
DropKind::Value { ref mut cached_block } => cached_block,
DropKind::Value { ref mut cached_block } => cached_block.ref_mut(generator_drop),
DropKind::Storage => continue
};
target = if let Some(cached_block) = *cached_block {

View file

@ -299,6 +299,11 @@ pub(crate) fn drop_flag_effects_for_location<'a, 'tcx, F>(
move_data.rev_lookup.find(location),
|moi| callback(moi, DropFlagState::Present))
}
mir::TerminatorKind::Suspend { .. } => {
on_lookup_result_bits(tcx, mir, move_data,
move_data.rev_lookup.find(&Mir::impl_arg_lvalue()),
|moi| callback(moi, DropFlagState::Present))
}
_ => {
// other terminators do not contain move-ins
}

View file

@ -452,15 +452,21 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
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::Suspend { 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::Suspend { resume: ref target, drop: Some(ref drop), .. } => {
self.propagate_bits_into_entry_set_for(in_out, changed, target);
self.propagate_bits_into_entry_set_for(in_out, changed, drop);
}
mir::TerminatorKind::Assert { ref target, cleanup: Some(ref unwind), .. } |
mir::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } |
mir::TerminatorKind::DropAndReplace {

View file

@ -462,6 +462,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
match term.kind {
TerminatorKind::Goto { target: _ } |
TerminatorKind::Resume |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable => { }
TerminatorKind::Return => {
@ -473,6 +474,11 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
// branching terminators - these don't move anything
}
TerminatorKind::Suspend { ref value, .. } => {
self.create_move_path(&Mir::impl_arg_lvalue());
self.gather_operand(loc, value);
}
TerminatorKind::Drop { ref location, target: _, unwind: _ } => {
self.gather_move(loc, location);
}

View file

@ -428,10 +428,11 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
}
}
hir::ExprClosure(..) => {
hir::ExprClosure(.., gen) => {
let closure_ty = cx.tables().expr_ty(expr);
let (def_id, substs) = match closure_ty.sty {
ty::TyClosure(def_id, substs) => (def_id, substs),
ty::TyClosure(def_id, substs) |
ty::TyGenerator(def_id, substs, _) => (def_id, substs),
_ => {
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
}
@ -446,6 +447,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
closure_id: def_id,
substs: substs,
upvars: upvars,
generator: gen.is_some(),
}
}
@ -564,6 +566,9 @@ 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::ExprImplArg(_) => ExprKind::ImplArg,
hir::ExprSuspend(ref v) => ExprKind::Suspend { value: v.to_ref() },
};
Expr {
@ -698,7 +703,8 @@ 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) {
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 {
@ -748,6 +754,14 @@ fn convert_var<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
kind: ExprKind::SelfRef,
}
}
}
} else {
Expr {
ty: closure_ty,
temp_lifetime: temp_lifetime,
span: expr.span,
kind: ExprKind::SelfRef,
}
};
// at this point we have `self.n`, which loads up the upvar

View file

@ -61,6 +61,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
let constness = match src {
MirSource::Const(_) |
MirSource::Static(..) => hir::Constness::Const,
MirSource::GeneratorDrop(..) => hir::Constness::NotConst,
MirSource::Fn(id) => {
let fn_like = FnLikeNode::from_node(infcx.tcx.hir.get(id));
fn_like.map_or(hir::Constness::NotConst, |f| f.constness())

View file

@ -239,6 +239,7 @@ pub enum ExprKind<'tcx> {
closure_id: DefId,
substs: ClosureSubsts<'tcx>,
upvars: Vec<ExprRef<'tcx>>,
generator: bool,
},
Literal {
literal: Literal<'tcx>,
@ -248,6 +249,10 @@ pub enum ExprKind<'tcx> {
outputs: Vec<ExprRef<'tcx>>,
inputs: Vec<ExprRef<'tcx>>
},
ImplArg,
Suspend {
value: ExprRef<'tcx>,
},
}
#[derive(Clone, Debug)]

View file

@ -148,6 +148,12 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
{
debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
// Check if this is a generator, if so, return the drop glue for it
if let Some(&ty::TyS { sty: ty::TyGenerator(gen_def_id, substs, _), .. }) = ty {
let mir = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap();
return mir.subst(tcx, substs.substs);
}
let substs = if let Some(ty) = ty {
tcx.mk_substs(iter::once(Kind::from(ty)))
} else {
@ -178,6 +184,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
),
IndexVec::new(),
sig.output(),
None,
local_decls_for_sig(&sig, span),
sig.inputs().len(),
vec![],
@ -213,10 +220,10 @@ fn build_drop_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
}
pub struct DropShimElaborator<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
patch: MirPatch<'tcx>,
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
pub mir: &'a Mir<'tcx>,
pub patch: MirPatch<'tcx>,
pub tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> {
@ -390,6 +397,7 @@ fn build_call_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
),
IndexVec::new(),
sig.output(),
None,
local_decls,
sig.inputs().len(),
vec![],
@ -461,6 +469,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
),
IndexVec::new(),
sig.output(),
None,
local_decls,
sig.inputs().len(),
vec![],

View file

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

View file

@ -96,15 +96,20 @@ 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 impl_arg = Mir::impl_arg_lvalue();
let location = match bb_data.terminator().kind {
TerminatorKind::Drop { ref location, unwind: Some(_), .. } |
TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => {
TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => location,
TerminatorKind::Suspend { .. } => &impl_arg,
_ => continue,
};
let mut init_data = InitializationData {
live: flow_inits.sets().on_entry_set_for(bb.index()).to_owned(),
dead: IdxSetBuf::new_empty(env.move_data.move_paths.len()),
};
debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
bb, bb_data, init_data.live);
bb, bb_data, init_data.live);
for stmt in 0..bb_data.statements.len() {
let loc = Location { block: bb, statement_index: stmt };
init_data.apply_location(tcx, mir, env, loc);
@ -130,9 +135,6 @@ fn find_dead_unwinds<'a, 'tcx>(
if !maybe_live {
dead_unwinds.add(&bb);
}
}
_ => {}
}
}
dead_unwinds
@ -342,9 +344,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
{
for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
let terminator = data.terminator();
let impl_arg = Mir::impl_arg_lvalue();
let location = match terminator.kind {
TerminatorKind::Drop { ref location, .. } |
TerminatorKind::DropAndReplace { ref location, .. } => location,
TerminatorKind::Suspend { .. } => &impl_arg,
_ => continue
};

View file

@ -0,0 +1,739 @@
// 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.
//! Transforming generator bodies into a state machines
#![allow(warnings)]
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::bitvec::BitVector;
use rustc_data_structures::indexed_vec::Idx;
use std::collections::HashMap;
use std::borrow::Cow;
use std::iter::once;
use syntax::ast::NodeId;
use transform::simplify;
pub struct StateTransform;
struct RenameLocalVisitor {
from: Local,
to: Local,
}
impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor {
fn visit_local(&mut self,
local: &mut Local) {
if *local == self.from {
*local = self.to;
}
}
}
struct SwapLocalVisitor {
a: Local,
b: Local,
}
impl<'tcx> MutVisitor<'tcx> for SwapLocalVisitor {
fn visit_local(&mut self,
local: &mut Local) {
if *local == self.a {
*local = self.b;
} else if *local == self.b {
*local = self.a;
}
}
}
struct InsertLocalVisitor {
local: Local,
}
impl<'tcx> MutVisitor<'tcx> for InsertLocalVisitor {
fn visit_local(&mut self,
local: &mut Local) {
if local.index() >= self.local.index() {
*local = Local::new(local.index() + 1);
}
}
}
struct DerefArgVisitor;
impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor {
fn visit_lvalue(&mut self,
lvalue: &mut Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: Location) {
if *lvalue == Lvalue::Local(Local::new(2)) {
*lvalue = Lvalue::Projection(Box::new(Projection {
base: lvalue.clone(),
elem: ProjectionElem::Deref,
}));
} else {
self.super_lvalue(lvalue, context, location);
}
}
}
struct TransformVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
state_adt_ref: &'tcx AdtDef,
state_substs: &'tcx Substs<'tcx>,
remap: HashMap<Local, (Ty<'tcx>, usize)>,
bb_target_count: u32,
bb_targets: HashMap<(BasicBlock, Option<BasicBlock>), u32>,
new_ret_local: Local,
return_block: BasicBlock,
state_field: usize,
}
impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
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])
}
fn make_field(&self, idx: usize, ty: Ty<'tcx>) -> Lvalue<'tcx> {
let base = Lvalue::Local(Local::new(1));
let base = Lvalue::Projection(Box::new(Projection {
base: base,
elem: ProjectionElem::Deref,
}));
let field = Projection {
base: base,
elem: ProjectionElem::Field(Field::new(idx), ty),
};
Lvalue::Projection(Box::new(field))
}
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 {
if let Some(&(ty, idx)) = self.remap.get(&l) {
*lvalue = self.make_field(idx, ty);
}
} else {
self.super_lvalue(lvalue, context, location);
}
}
fn visit_basic_block_data(&mut self,
block: BasicBlock,
data: &mut BasicBlockData<'tcx>) {
let ret_val = match data.terminator().kind {
TerminatorKind::Return => Some((1,
self.return_block,
Operand::Consume(Lvalue::Local(self.new_ret_local)),
None)),
TerminatorKind::Suspend { ref value, resume, drop } => Some((0,
resume,
value.clone(),
drop)),
_ => None
};
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
}
});
ret_val.map(|(state_idx, resume, v, drop)| {
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 get_body_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, node_id: NodeId) -> (bool, hir::BodyId) {
// Figure out what primary body this item has.
match tcx.hir.get(node_id) {
hir::map::NodeItem(item) => {
match item.node {
hir::ItemConst(_, body) |
hir::ItemStatic(_, _, body) |
hir::ItemFn(.., body) => (false, body),
_ => bug!(),
}
}
hir::map::NodeTraitItem(item) => {
match item.node {
hir::TraitItemKind::Const(_, Some(body)) |
hir::TraitItemKind::Method(_,
hir::TraitMethod::Provided(body)) => (false, body),
_ => bug!(),
}
}
hir::map::NodeImplItem(item) => {
match item.node {
hir::ImplItemKind::Const(_, body) |
hir::ImplItemKind::Method(_, body) => (false, body),
_ => bug!(),
}
}
hir::map::NodeExpr(expr) => {
// FIXME(eddyb) Closures should have separate
// function definition IDs and expression IDs.
// Type-checking should not let closures get
// this far in a constant position.
// Assume that everything other than closures
// is a constant "initializer" expression.
match expr.node {
hir::ExprClosure(_, _, body, _, _) => (true, body),
_ => (false, hir::BodyId { node_id: expr.id })
}
}
_ => bug!(),
}
}
fn ensure_generator_state_argument<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
node_id: NodeId,
def_id: DefId,
mir: &mut Mir<'tcx>) -> (Ty<'tcx>, GeneratorInterior<'tcx>) {
let interior = *tcx.typeck_tables_of(def_id).generator_interiors.get(&node_id).unwrap();
let gen_ty = mir.local_decls.raw[2].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[2].ty = ref_gen_ty;
// Add a deref to accesses of the generator state for upvars
DerefArgVisitor.visit_mir(mir);
// Swap generator and implicit argument
SwapLocalVisitor {
a: Local::new(1),
b: Local::new(2),
}.visit_mir(mir);
mir.local_decls.raw[..].swap(1, 2);
(gen_ty, interior)
}
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,
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 {
use rustc_data_structures::indexed_set::IdxSetBuf;
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::Suspend { .. } = data.terminator().kind {
set.union(&result.outs[block]);
}
}
// The implicit argument is defined after each suspend point so it can never be live in a suspend point.
set.remove(&Local::new(2));
// The generator argument is ignored
set.remove(&Local::new(1));
set
}
fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
source: MirSource,
interior: GeneratorInterior<'tcx>,
mir: &mut Mir<'tcx>) -> (HashMap<Local, (Ty<'tcx>, usize)>, GeneratorLayout<'tcx>) {
let source_info = SourceInfo {
span: mir.span,
scope: ARGUMENT_VISIBILITY_SCOPE,
};
let mut live_locals = locals_live_across_suspend_points(tcx, mir, source);
let allowed = tcx.erase_regions(&interior.as_slice());
for (local, decl) in mir.local_decls.iter_enumerated() {
if !live_locals.contains(&local) {
continue;
}
if !allowed.contains(&decl.ty) {
tcx.sess.span_warn(mir.span,
&format!("generator contains type {} in MIR, but typeck only knows about {}",
decl.ty,
interior));
}
}
let upvar_len = mir.upvar_decls.len();
let live_decls : Vec<_> = mir.local_decls.iter_enumerated_mut().filter(|&(local, _)| live_locals.contains(&local)).collect();
let mut remap = HashMap::new();
let unit = tcx.mk_nil();
let mut vars: Vec<_> = live_decls.into_iter().enumerate().map(|(idx, (local, decl))| {
let var = decl.clone();
*decl = LocalDecl {
mutability: Mutability::Mut,
ty: unit,
name: None,
source_info,
is_user_variable: false,
};
remap.insert(local, (var.ty, upvar_len + 1 + idx));
var
}).collect();
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, DropElaborator, DropStyle, DropFlagMode};
use util::patch::MirPatch;
use shim::DropShimElaborator;
let param_env = tcx.param_env(def_id);
let gen = Local::new(2);
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 generate_drop<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
transform: &TransformVisitor<'a, 'tcx>,
node_id: NodeId,
def_id: DefId,
source: MirSource,
gen_ty: Ty<'tcx>,
mir: &mut Mir<'tcx>) {
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: TerminatorKind::Return,
}),
is_cleanup: false,
});
let cases: Vec<_> = transform.bb_targets.iter().filter_map(|(&(r, u), &s)| {
u.map(|d| (s, d))
}).collect();
// 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(transform.return_block)).collect(),
};
insert_entry_point(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;
}
}
// Remove the implicit argument
mir.arg_count = 1;
mir.local_decls.raw.pop();
// 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,
is_user_variable: false,
};
// Change the generator argument from &mut to *mut
mir.local_decls[Local::new(1)] = LocalDecl {
mutability: Mutability::Mut,
ty: tcx.mk_ptr(ty::TypeAndMut {
ty: gen_ty,
mutbl: hir::Mutability::MutMutable,
}),
name: None,
source_info,
is_user_variable: false,
};
// Make sure we remove dead blocks to remove
// unrelated code from the resume part of the function
simplify::remove_dead_blocks(mir);
dump_mir(tcx, None, "generator_drop", &0, source, mir);
}
fn generate_resume<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mut transform: TransformVisitor<'a, 'tcx>,
node_id: NodeId,
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 drop_arg = mir.local_decls.raw[2].ty.needs_drop(tcx, tcx.param_env(def_id));
let cleanup = if drop_arg {
Some(BasicBlock::new(mir.basic_blocks().len() + 1))
} else {
None
};
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: transform.return_block,
cleanup: cleanup,
};
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,
});
if drop_arg {
let resume_block = BasicBlock::new(mir.basic_blocks().len() + 1);
let term = TerminatorKind::Drop {
location: Lvalue::Local(Local::new(2)),
target: resume_block,
unwind: None,
};
mir.basic_blocks_mut().push(BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
source_info,
kind: term,
}),
is_cleanup: true,
});
mir.basic_blocks_mut().push(BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
source_info,
kind: TerminatorKind::Resume,
}),
is_cleanup: true,
});
}
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: cleanup,
};
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 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);
}
impl MirPass for StateTransform {
fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: MirSource,
mir: &mut Mir<'tcx>) {
let suspend_ty = if let Some(suspend_ty) = mir.suspend_ty {
suspend_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());
elaborate_generator_drops(tcx, def_id, mir);
let (gen_ty, interior) = ensure_generator_state_argument(tcx, node_id, def_id, mir);
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(suspend_ty),
Kind::from(mir.return_ty)].iter());
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
let new_ret_local = replace_result_variable(ret_ty, mir);
let (remap, layout) = compute_layout(tcx, def_id, source, interior, mir);
let return_block = BasicBlock::new(mir.basic_blocks().len());
let state_field = mir.upvar_decls.len();
let mut bb_targets = HashMap::new();
bb_targets.insert((BasicBlock::new(0), None), 0);
let mut transform = TransformVisitor {
tcx,
state_adt_ref,
state_substs,
remap,
bb_target_count: 2,
bb_targets,
new_ret_local,
return_block,
state_field,
};
transform.visit_mir(mir);
mir.return_ty = ret_ty;
mir.suspend_ty = None;
mir.arg_count = 2;
mir.spread_arg = None;
mir.generator_layout = Some(layout);
dump_mir(tcx, None, "generator_post-transform", &0, source, mir);
let mut drop_impl = mir.clone();
generate_drop(tcx, &transform, node_id, def_id, source, gen_ty, &mut drop_impl);
mir.generator_drop = Some(box drop_impl);
generate_resume(tcx, transform, node_id, def_id, source, mir);
}
}

View file

@ -175,6 +175,10 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
return false;
}
// Cannot inline generators which haven't been transformed yet
if callee_mir.suspend_ty.is_some() {
return false;
}
let attrs = tcx.get_attrs(callsite.callee);
let hint = attr::find_inline_attr(None, &attrs[..]);
@ -652,6 +656,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
self.super_terminator_kind(block, kind, loc);
match *kind {
TerminatorKind::GeneratorDrop |
TerminatorKind::Suspend { .. } => bug!(),
TerminatorKind::Goto { ref mut target} => {
*target = self.update_target(*target);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -34,7 +34,7 @@ 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!($($message)*)))
})
}
@ -60,6 +60,7 @@ struct TypeVerifier<'a, 'b: 'a, 'gcx: 'b+'tcx, 'tcx: 'b> {
cx: &'a mut TypeChecker<'b, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
last_span: Span,
body_id: ast::NodeId,
errors_reported: bool
}
@ -108,6 +109,7 @@ 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 {
body_id: cx.body_id,
cx: cx,
mir: mir,
last_span: mir.span,
@ -297,6 +299,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),
@ -427,6 +442,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
@ -493,6 +509,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
}
TerminatorKind::Suspend { ref value, .. } => {
let value_ty = value.ty(mir, tcx);
match mir.suspend_ty {
None => span_mirbug!(self, term, "suspend in non-generator"),
Some(ty) if ty != value_ty => {
span_mirbug!(self,
term,
"type of suspend value is ({:?}, but the suspend type is ({:?}",
value_ty,
ty);
}
_ => (),
}
}
}
}
@ -619,6 +649,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::Suspend { resume, drop, .. } => {
if is_cleanup {
span_mirbug!(self, block, "suspend in cleanup block")
}
self.assert_iscleanup(mir, block, resume, is_cleanup);
if let Some(drop) = drop {
self.assert_iscleanup(mir, block, drop, is_cleanup);
}
}
TerminatorKind::Unreachable => {}
TerminatorKind::Drop { target, unwind, .. } |
TerminatorKind::DropAndReplace { target, unwind, .. } |

View file

@ -752,7 +752,8 @@ 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) |
ty::TyGenerator(def_id, substs, _) => {
let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
self.open_drop_for_tuple(&tys)
}

View file

@ -0,0 +1,205 @@
// 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.
// FIXME: Make sure this analysis uses proper MIR semantics. Also find out what the MIR semantics are.
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 | LvalueContext::Call => {
self.defs.add(&local);
}
LvalueContext::Projection(..) |
LvalueContext::Borrow { .. } |
LvalueContext::Inspect |
LvalueContext::Consume |
LvalueContext::Drop => {
// Ignore uses which are already defined in this block
if !self.pre_defs.contains(&local) {
self.uses.add(&local);
}
}
LvalueContext::StorageLive | LvalueContext::StorageDead => (),
}
}
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,
}
}
pub struct LivenessResult {
pub ins: IndexVec<BasicBlock, LocalSet>,
pub outs: IndexVec<BasicBlock, LocalSet>,
}
pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
let locals = mir.local_decls.len();
let def_use: IndexVec<_, _> = mir.basic_blocks().iter().map(|b| {
block(b, locals)
}).collect();
let copy = |from: &IndexVec<BasicBlock, LocalSet>, to: &mut IndexVec<BasicBlock, LocalSet>| {
for (i, set) in to.iter_enumerated_mut() {
set.clone_from(&from[i]);
}
};
let mut ins: IndexVec<_, _> = mir.basic_blocks()
.indices()
.map(|_| LocalSet::new_empty(locals)).collect();
let mut outs = ins.clone();
let mut ins_ = ins.clone();
let mut outs_ = outs.clone();
loop {
copy(&ins, &mut ins_);
copy(&outs, &mut outs_);
for b in mir.basic_blocks().indices().rev() {
// out = {ins of successors}
outs[b].clear();
for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
outs[b].union(&ins[successor]);
}
// in = use (out - def)
ins[b].clone_from(&outs[b]);
ins[b].subtract(&def_use[b].defs);
ins[b].union(&def_use[b].uses);
}
if ins_ == ins && outs_ == outs {
break;
}
}
LivenessResult {
ins,
outs,
}
}
pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult) {
if !dump_enabled(tcx, pass_name, source) {
return;
}
let node_path = item_path::with_forced_impl_filename_line(|| { // see notes on #41697 below
tcx.item_path_str(tcx.hir.local_def_id(source.item_id()))
});
dump_matched_mir_node(tcx, pass_name, &node_path,
source, mir, result);
}
fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
pass_name: &str,
node_path: &str,
source: MirSource,
mir: &Mir<'tcx>,
result: &LivenessResult) {
let mut file_path = PathBuf::new();
if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
let p = Path::new(file_dir);
file_path.push(p);
};
let file_name = format!("rustc.node{}{}-liveness.mir",
source.item_id(), pass_name);
file_path.push(&file_name);
let _ = fs::File::create(&file_path).and_then(|mut file| {
writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
writeln!(file, "// source = {:?}", source)?;
writeln!(file, "// pass_name = {}", pass_name)?;
writeln!(file, "")?;
write_mir_fn(tcx, source, mir, &mut file, result)?;
Ok(())
});
}
pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
w: &mut Write,
result: &LivenessResult)
-> io::Result<()> {
write_mir_intro(tcx, src, mir, w)?;
for block in mir.basic_blocks().indices() {
let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
let live: Vec<String> = mir.local_decls.indices().filter(|i| result[block].contains(i)).map(|i| format!("{:?}", i)).collect();
writeln!(w, "{} {{{}}}", prefix, live.join(", "))
};
print(w, " ", &result.ins)?;
write_basic_block(tcx, block, mir, w)?;
print(w, " ", &result.outs)?;
if block.index() + 1 != mir.basic_blocks().len() {
writeln!(w, "")?;
}
}
writeln!(w, "}}")?;
Ok(())
}

View file

@ -14,6 +14,7 @@ pub mod patch;
mod graphviz;
mod pretty;
pub mod liveness;
pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty};
pub use self::graphviz::{write_mir_graphviz};

View file

@ -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,32 @@ 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)
}
_ => {
assert_eq!(mir.arg_count, 0);
write!(w, ": {} =", mir.return_ty)
}
}
}

View file

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

View file

@ -81,7 +81,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
hir::ExprLoop(ref b, _, source) => {
self.with_context(Loop(LoopKind::Loop(source)), |v| v.visit_block(&b));
}
hir::ExprClosure(.., b, _) => {
hir::ExprClosure(.., b, _, _) => {
self.with_context(Closure, |v| v.visit_nested_body(b));
}
hir::ExprBreak(label, ref opt_expr) => {

View file

@ -158,6 +158,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::Suspend { .. } => "TerminatorKind::Suspend",
}, kind);
self.super_terminator_kind(block, kind, location);
}
@ -169,6 +171,8 @@ 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);
}
@ -196,6 +200,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
AggregateKind::Tuple => "AggregateKind::Tuple",
AggregateKind::Adt(..) => "AggregateKind::Adt",
AggregateKind::Closure(..) => "AggregateKind::Closure",
AggregateKind::Generator(..) => "AggregateKind::Generator",
}, kind);
"Rvalue::Aggregate"

View file

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

View file

@ -287,7 +287,9 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance
let hash = get_symbol_hash(tcx, Some(def_id), instance_ty, Some(substs));
SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash)
let buffer = SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id));
buffer.finish(hash)
}
// Follow C++ namespace-mangling style, see

View file

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

View file

@ -28,12 +28,13 @@ use type_::Type;
use value::Value;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::{Layout, LayoutTyper};
use rustc::ty::subst::{Subst, Substs};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::hir;
use libc::{c_uint, c_char};
use std::iter;
use syntax::abi::Abi;
use syntax::attr;
use syntax::symbol::InternedString;
use syntax_pos::Span;
@ -91,6 +92,16 @@ pub fn type_pair_fields<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>)
}
}))
}
ty::TyGenerator(def_id, substs, _) => {
let mut tys = substs.field_tys(def_id, ccx.tcx());
tys.next().and_then(|first_ty| tys.next().and_then(|second_ty| {
if tys.next().is_some() {
None
} else {
Some([first_ty, second_ty])
}
}))
}
ty::TyTuple(tys, _) => {
if tys.len() != 2 {
return None;
@ -517,6 +528,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.suspend_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).chain(iter::once(sig.impl_arg_ty)),
ret_ty,
false,
hir::Unsafety::Normal,
Abi::Rust
)
})
}
_ => bug!("unexpected type {:?} to ty_fn_sig", ty)
}
}

View file

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

View file

@ -165,6 +165,9 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
ty::TyClosure(..) => {
output.push_str("closure");
}
ty::TyGenerator(..) => {
output.push_str("generator");
}
ty::TyError |
ty::TyInfer(_) |
ty::TyProjection(..) |

View file

@ -215,8 +215,10 @@ pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock
TerminatorKind::Goto { .. } |
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::GeneratorDrop |
TerminatorKind::Unreachable |
TerminatorKind::SwitchInt { .. } => {
TerminatorKind::SwitchInt { .. } |
TerminatorKind::Suspend { .. } => {
/* nothing to do */
}
TerminatorKind::Call { cleanup: unwind, .. } |

View file

@ -374,6 +374,27 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
vec![msg_file_line_col],
Some(ErrKind::Math(err.clone())))
}
mir::AssertMessage::GeneratorResumedAfterReturn |
mir::AssertMessage::GeneratorResumedAfterPanic => {
let str = if let mir::AssertMessage::GeneratorResumedAfterReturn = *msg {
"generator resumed after completion"
} else {
"generator resumed after panicking"
};
let msg_str = Symbol::intern(str).as_str();
let msg_str = C_str_slice(bcx.ccx, msg_str);
let msg_file_line = C_struct(bcx.ccx,
&[msg_str, filename, line],
false);
let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line));
let msg_file_line = consts::addr_of(bcx.ccx,
msg_file_line,
align,
"panic_loc");
(lang_items::PanicFnLangItem,
vec![msg_file_line],
None)
}
};
// If we know we always panic, and the error message
@ -557,6 +578,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
destination.as_ref().map(|&(_, target)| (ret_dest, sig.output(), target)),
cleanup);
}
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Suspend { .. } => bug!("generator ops in trans"),
}
}

View file

@ -322,6 +322,9 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
mir::AssertMessage::Math(ref err) => {
ErrKind::Math(err.clone())
}
mir::AssertMessage::GeneratorResumedAfterReturn |
mir::AssertMessage::GeneratorResumedAfterPanic =>
span_bug!(span, "{:?} should not appear in constants?", msg),
};
let err = ConstEvalErr { span: span, kind: err };
@ -565,6 +568,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
}
mir::AggregateKind::Adt(..) |
mir::AggregateKind::Closure(..) |
mir::AggregateKind::Generator(..) |
mir::AggregateKind::Tuple => {
Const::new(trans_const(self.ccx, dest_ty, kind, &fields), dest_ty)
}

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