Skip to content

Commit

Permalink
Adds support for immovable generators. Move checking of invalid borro…
Browse files Browse the repository at this point in the history
…ws across suspension points to borrowck. Fixes rust-lang#44197, rust-lang#45259 and rust-lang#45093.
  • Loading branch information
Zoxc committed Jan 23, 2018
1 parent 47a8eb7 commit ccf0d83
Show file tree
Hide file tree
Showing 60 changed files with 867 additions and 217 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 @@ -2768,7 +2768,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 @@ -2777,16 +2777,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 ccf0d83

Please sign in to comment.