Skip to content

Commit

Permalink
Auto merge of #45337 - Zoxc:gen-static, r=nikomatsakis
Browse files Browse the repository at this point in the history
Immovable generators

This adds support for immovable generators which allow you to borrow local values inside generator across suspension points. These are declared using a `static` keyword:
```rust
let mut generator = static || {
    let local = &Vec::new();
    yield;
    local.push(0i8);
};
generator.resume();

// ERROR moving the generator after it has resumed would invalidate the interior reference
// drop(generator);
```

Region inference is no longer affected by the types stored in generators so the regions inside should be similar to other code (and unaffected by the presence of `yield` expressions). The borrow checker is extended to pick up the slack so interior references still result in errors for movable generators. This fixes #44197, #45259 and #45093.

This PR depends on [PR #44917 (immovable types)](#44917), I suggest potential reviewers ignore the first commit as it adds immovable types.
  • Loading branch information
bors committed Jan 23, 2018
2 parents 4e3901d + 55c6c88 commit fdecb05
Show file tree
Hide file tree
Showing 71 changed files with 1,116 additions and 228 deletions.
2 changes: 2 additions & 0 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,4 +2118,6 @@ register_diagnostics! {
E0657, // `impl Trait` can only capture lifetimes bound at the fn level
E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax
E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders

E0906, // closures cannot be static
}
26 changes: 19 additions & 7 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2773,7 +2773,7 @@ impl<'a> LoweringContext<'a> {
arms.iter().map(|x| self.lower_arm(x)).collect(),
hir::MatchSource::Normal)
}
ExprKind::Closure(capture_clause, ref decl, ref body, fn_decl_span) => {
ExprKind::Closure(capture_clause, movability, ref decl, ref body, fn_decl_span) => {
self.with_new_scopes(|this| {
this.with_parent_def(e.id, |this| {
let mut is_generator = false;
Expand All @@ -2782,16 +2782,28 @@ impl<'a> LoweringContext<'a> {
is_generator = this.is_generator;
e
});
if is_generator && !decl.inputs.is_empty() {
span_err!(this.sess, fn_decl_span, E0628,
"generators cannot have explicit arguments");
this.sess.abort_if_errors();
}
let generator_option = if is_generator {
if !decl.inputs.is_empty() {
span_err!(this.sess, fn_decl_span, E0628,
"generators cannot have explicit arguments");
this.sess.abort_if_errors();
}
Some(match movability {
Movability::Movable => hir::GeneratorMovability::Movable,
Movability::Static => hir::GeneratorMovability::Static,
})
} else {
if movability == Movability::Static {
span_err!(this.sess, fn_decl_span, E0906,
"closures cannot be static");
}
None
};
hir::ExprClosure(this.lower_capture_clause(capture_clause),
this.lower_fn_decl(decl, None, false),
body_id,
fn_decl_span,
is_generator)
generator_option)
})
})
}
Expand Down
8 changes: 7 additions & 1 deletion src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ pub enum Expr_ {
///
/// This may also be a generator literal, indicated by the final boolean,
/// in that case there is an GeneratorClause.
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, bool),
ExprClosure(CaptureClause, P<FnDecl>, BodyId, Span, Option<GeneratorMovability>),
/// A block (`{ ... }`)
ExprBlock(P<Block>),

Expand Down Expand Up @@ -1466,6 +1466,12 @@ pub struct Destination {
pub target_id: ScopeTarget,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum GeneratorMovability {
Static,
Movable,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum CaptureClause {
CaptureByValue,
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/ich/impls_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,11 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::MatchSource {
}
}

impl_stable_hash_for!(enum hir::GeneratorMovability {
Static,
Movable
});

impl_stable_hash_for!(enum hir::CaptureClause {
CaptureByValue,
CaptureByRef
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ for ::middle::const_val::ErrKind<'gcx> {

impl_stable_hash_for!(struct ty::ClosureSubsts<'tcx> { substs });

impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness });
impl_stable_hash_for!(struct ty::GeneratorInterior<'tcx> { witness, movable });

impl_stable_hash_for!(struct ty::GenericPredicates<'tcx> {
parent,
Expand Down Expand Up @@ -656,6 +656,9 @@ for ty::TypeVariants<'gcx>
closure_substs.hash_stable(hcx, hasher);
interior.hash_stable(hcx, hasher);
}
TyGeneratorWitness(types) => {
types.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
if let Some(node_id) = hir.as_local_node_id(free_region.scope) {
match hir.get(node_id) {
NodeExpr(Expr {
node: ExprClosure(_, _, _, closure_span, false),
node: ExprClosure(_, _, _, closure_span, None),
..
}) => {
let sup_sp = sup_origin.span();
Expand Down
1 change: 1 addition & 0 deletions src/librustc/infer/freshen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
ty::TyForeign(..) |
ty::TyParam(..) |
ty::TyClosure(..) |
ty::TyGeneratorWitness(..) |
ty::TyAnon(..) => {
t.super_fold_with(self)
}
Expand Down
88 changes: 84 additions & 4 deletions src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,43 @@ struct RegionResolutionVisitor<'a, 'tcx: 'a> {
terminating_scopes: FxHashSet<hir::ItemLocalId>,
}

struct ExprLocatorVisitor {
id: ast::NodeId,
result: Option<usize>,
expr_and_pat_count: usize,
}

// This visitor has to have the same visit_expr calls as RegionResolutionVisitor
// since `expr_count` is compared against the results there.
impl<'tcx> Visitor<'tcx> for ExprLocatorVisitor {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}

fn visit_pat(&mut self, pat: &'tcx Pat) {
self.expr_and_pat_count += 1;

intravisit::walk_pat(self, pat);
}

fn visit_expr(&mut self, expr: &'tcx Expr) {
debug!("ExprLocatorVisitor - pre-increment {} expr = {:?}",
self.expr_and_pat_count,
expr);

intravisit::walk_expr(self, expr);

self.expr_and_pat_count += 1;

debug!("ExprLocatorVisitor - post-increment {} expr = {:?}",
self.expr_and_pat_count,
expr);

if expr.id == self.id {
self.result = Some(self.expr_and_pat_count);
}
}
}

impl<'tcx> ScopeTree {
pub fn record_scope_parent(&mut self, child: Scope, parent: Option<Scope>) {
Expand Down Expand Up @@ -612,6 +649,20 @@ impl<'tcx> ScopeTree {
return true;
}

/// Returns the id of the innermost containing body
pub fn containing_body(&self, mut scope: Scope)-> Option<hir::ItemLocalId> {
loop {
if let ScopeData::CallSite(id) = scope.data() {
return Some(id);
}

match self.opt_encl_scope(scope) {
None => return None,
Some(parent) => scope = parent,
}
}
}

/// Finds the nearest common ancestor (if any) of two scopes. That is, finds the smallest
/// scope which is greater than or equal to both `scope_a` and `scope_b`.
pub fn nearest_common_ancestor(&self,
Expand Down Expand Up @@ -768,6 +819,28 @@ impl<'tcx> ScopeTree {
self.yield_in_scope.get(&scope).cloned()
}

/// Checks whether the given scope contains a `yield` and if that yield could execute
/// after `expr`. If so, it returns the span of that `yield`.
/// `scope` must be inside the body.
pub fn yield_in_scope_for_expr(&self,
scope: Scope,
expr: ast::NodeId,
body: &'tcx hir::Body) -> Option<Span> {
self.yield_in_scope(scope).and_then(|(span, count)| {
let mut visitor = ExprLocatorVisitor {
id: expr,
result: None,
expr_and_pat_count: 0,
};
visitor.visit_body(body);
if count >= visitor.result.unwrap() {
Some(span)
} else {
None
}
})
}

/// Gives the number of expressions visited in a body.
/// Used to sanity check visit_expr call count when
/// calculating generator interiors.
Expand Down Expand Up @@ -872,9 +945,13 @@ fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &
record_var_lifetime(visitor, pat.hir_id.local_id, pat.span);
}

debug!("resolve_pat - pre-increment {} pat = {:?}", visitor.expr_and_pat_count, pat);

intravisit::walk_pat(visitor, pat);

visitor.expr_and_pat_count += 1;

debug!("resolve_pat - post-increment {} pat = {:?}", visitor.expr_and_pat_count, pat);
}

fn resolve_stmt<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
Expand All @@ -897,7 +974,7 @@ fn resolve_stmt<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, stmt:
}

fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr: &'tcx hir::Expr) {
debug!("resolve_expr(expr.id={:?})", expr.id);
debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);

let prev_cx = visitor.cx;
visitor.enter_node_scope_with_dtor(expr.hir_id.local_id);
Expand Down Expand Up @@ -982,6 +1059,8 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:

visitor.expr_and_pat_count += 1;

debug!("resolve_expr post-increment {}, expr = {:?}", visitor.expr_and_pat_count, expr);

if let hir::ExprYield(..) = expr.node {
// Mark this expr's scope and all parent scopes as containing `yield`.
let mut scope = Scope::Node(expr.hir_id.local_id);
Expand Down Expand Up @@ -1077,12 +1156,13 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
}
}

if let Some(pat) = pat {
visitor.visit_pat(pat);
}
// Make sure we visit the initializer first, so expr_and_pat_count remains correct
if let Some(expr) = init {
visitor.visit_expr(expr);
}
if let Some(pat) = pat {
visitor.visit_pat(pat);
}

/// True if `pat` match the `P&` nonterminal:
///
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,10 @@ fn ty_is_local_constructor(ty: Ty, in_crate: InCrate) -> bool {
true
}

ty::TyClosure(..) | ty::TyGenerator(..) | ty::TyAnon(..) => {
ty::TyClosure(..) |
ty::TyGenerator(..) |
ty::TyGeneratorWitness(..) |
ty::TyAnon(..) => {
bug!("ty_is_local invoked on unexpected type: {:?}", ty)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
},
ty::TyGenerator(..) => Some(18),
ty::TyForeign(..) => Some(19),
ty::TyGeneratorWitness(..) => Some(20),
ty::TyInfer(..) | ty::TyError => None
}
}
Expand Down
16 changes: 11 additions & 5 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2044,8 +2044,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
ty::TyUint(_) | ty::TyInt(_) | ty::TyBool | ty::TyFloat(_) |
ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyRawPtr(..) |
ty::TyChar | ty::TyRef(..) | ty::TyGenerator(..) |
ty::TyArray(..) | ty::TyClosure(..) | ty::TyNever |
ty::TyError => {
ty::TyGeneratorWitness(..) | ty::TyArray(..) | ty::TyClosure(..) |
ty::TyNever | ty::TyError => {
// safe for everything
Where(ty::Binder(Vec::new()))
}
Expand Down Expand Up @@ -2095,7 +2095,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}

ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
ty::TyGenerator(..) | ty::TyForeign(..) |
ty::TyGenerator(..) | ty::TyGeneratorWitness(..) | ty::TyForeign(..) |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
Never
}
Expand Down Expand Up @@ -2206,8 +2206,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}

ty::TyGenerator(def_id, ref substs, interior) => {
let witness = iter::once(interior.witness);
substs.upvar_tys(def_id, self.tcx()).chain(witness).collect()
substs.upvar_tys(def_id, self.tcx()).chain(iter::once(interior.witness)).collect()
}

ty::TyGeneratorWitness(types) => {
// This is sound because no regions in the witness can refer to
// the binder outside the witness. So we'll effectivly reuse
// the implicit binder around the witness.
types.skip_binder().to_vec()
}

// for `PhantomData<T>`, we pass `T`
Expand Down
9 changes: 7 additions & 2 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1672,8 +1672,9 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
pub fn print_debug_stats(self) {
sty_debug_print!(
self,
TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyGenerator, TyForeign,
TyDynamic, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon);
TyAdt, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr,
TyGenerator, TyGeneratorWitness, TyDynamic, TyClosure, TyTuple,
TyParam, TyInfer, TyProjection, TyAnon, TyForeign);

println!("Substs interner: #{}", self.interners.substs.borrow().len());
println!("Region interner: #{}", self.interners.region.borrow().len());
Expand Down Expand Up @@ -2079,6 +2080,10 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.mk_ty(TyGenerator(id, closure_substs, interior))
}

pub fn mk_generator_witness(self, types: ty::Binder<&'tcx Slice<Ty<'tcx>>>) -> Ty<'tcx> {
self.mk_ty(TyGeneratorWitness(types))
}

pub fn mk_var(self, v: TyVid) -> Ty<'tcx> {
self.mk_infer(TyVar(v))
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
}
ty::TyClosure(..) => "closure".to_string(),
ty::TyGenerator(..) => "generator".to_string(),
ty::TyGeneratorWitness(..) => "generator witness".to_string(),
ty::TyTuple(..) => "tuple".to_string(),
ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(),
ty::TyInfer(ty::IntVar(_)) => "integral variable".to_string(),
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/fast_reject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub enum SimplifiedTypeGen<D>
TraitSimplifiedType(D),
ClosureSimplifiedType(D),
GeneratorSimplifiedType(D),
GeneratorWitnessSimplifiedType(usize),
AnonSimplifiedType(D),
FunctionSimplifiedType(usize),
ParameterSimplifiedType,
Expand Down Expand Up @@ -92,6 +93,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
ty::TyGenerator(def_id, _, _) => {
Some(GeneratorSimplifiedType(def_id))
}
ty::TyGeneratorWitness(ref tys) => {
Some(GeneratorWitnessSimplifiedType(tys.skip_binder().len()))
}
ty::TyNever => Some(NeverSimplifiedType),
ty::TyTuple(ref tys, _) => {
Some(TupleSimplifiedType(tys.len()))
Expand Down Expand Up @@ -141,6 +145,7 @@ impl<D: Copy + Debug + Ord + Eq + Hash> SimplifiedTypeGen<D> {
TraitSimplifiedType(d) => TraitSimplifiedType(map(d)),
ClosureSimplifiedType(d) => ClosureSimplifiedType(map(d)),
GeneratorSimplifiedType(d) => GeneratorSimplifiedType(map(d)),
GeneratorWitnessSimplifiedType(n) => GeneratorWitnessSimplifiedType(n),
AnonSimplifiedType(d) => AnonSimplifiedType(map(d)),
FunctionSimplifiedType(n) => FunctionSimplifiedType(n),
ParameterSimplifiedType => ParameterSimplifiedType,
Expand Down Expand Up @@ -175,6 +180,7 @@ impl<'gcx, D> HashStable<StableHashingContext<'gcx>> for SimplifiedTypeGen<D>
TraitSimplifiedType(d) => d.hash_stable(hcx, hasher),
ClosureSimplifiedType(d) => d.hash_stable(hcx, hasher),
GeneratorSimplifiedType(d) => d.hash_stable(hcx, hasher),
GeneratorWitnessSimplifiedType(n) => n.hash_stable(hcx, hasher),
AnonSimplifiedType(d) => d.hash_stable(hcx, hasher),
FunctionSimplifiedType(n) => n.hash_stable(hcx, hasher),
ForeignSimplifiedType(d) => d.hash_stable(hcx, hasher),
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ impl FlagComputation {
self.add_ty(interior.witness);
}

&ty::TyGeneratorWitness(ref ts) => {
let mut computation = FlagComputation::new();
computation.add_tys(&ts.skip_binder()[..]);
self.add_bound_computation(&computation);
}

&ty::TyClosure(_, ref substs) => {
self.add_flags(TypeFlags::HAS_TY_CLOSURE);
self.add_flags(TypeFlags::HAS_LOCAL_NAMES);
Expand Down
Loading

0 comments on commit fdecb05

Please sign in to comment.