Skip to content

Commit

Permalink
address review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
arielb1 committed Sep 20, 2017
1 parent 5c0feb8 commit 018525e
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 16 deletions.
5 changes: 4 additions & 1 deletion src/librustc/hir/intravisit.rs
Expand Up @@ -411,10 +411,13 @@ pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body) {
}

pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) {
// Intentionally visiting the expr first - the initialization expr
// dominates the local's definition.
walk_list!(visitor, visit_expr, &local.init);

visitor.visit_id(local.id);
visitor.visit_pat(&local.pat);
walk_list!(visitor, visit_ty, &local.ty);
walk_list!(visitor, visit_expr, &local.init);
}

pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) {
Expand Down
66 changes: 53 additions & 13 deletions src/librustc/middle/region.rs
Expand Up @@ -259,8 +259,37 @@ pub struct ScopeTree {
/// lower than theirs, and therefore don't need to be suspended
/// at yield-points at these indexes.
///
/// Let's show that: let `D` be our binding/temporary and `U` be our
/// other HIR node, with `HIR-postorder(U) < HIR-postorder(D)`.
/// For an example, suppose we have some code such as:
/// ```rust,ignore (example)
/// foo(f(), yield y, bar(g()))
/// ```
///
/// With the HIR tree (calls numbered for expository purposes)
/// ```
/// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))])
/// ```
///
/// Obviously, the result of `f()` was created before the yield
/// (and therefore needs to be kept valid over the yield) while
/// the result of `g()` occurs after the yield (and therefore
/// doesn't). If we want to infer that, we can look at the
/// postorder traversal:
/// ```
/// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0
/// ```
///
/// In which we can easily see that `Call#1` occurs before the yield,
/// and `Call#3` after it.
///
/// To see that this method works, consider:
///
/// Let `D` be our binding/temporary and `U` be our other HIR node, with
/// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be
/// the yield and D would be one of the calls). Let's show that
/// `D` is storage-dead at `U`.
///
/// Remember that storage-live/storage-dead refers to the state of
/// the *storage*, and does not consider moves/drop flags.
///
/// Then:
/// 1. From the ordering guarantee of HIR visitors (see
Expand All @@ -272,17 +301,26 @@ pub struct ScopeTree {
/// or always storage-dead. This is what is being guaranteed
/// by `terminating_scopes` including all blocks where the
/// count of executions is not guaranteed.
/// 4. By `2.` and `3.`, `D` is *statically* dead at `U`,
/// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`,
/// QED.
///
/// I don't think this property relies on `3.` in an essential way - it
/// is probably still correct even if we have "unrestricted" terminating
/// scopes. However, why use the complicated proof when a simple one
/// works?
///
/// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It
/// might seem that a `box` expression creates a `Box<T>` temporary
/// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might
/// be true in the MIR desugaring, but it is not important in the semantics.
///
/// The reason is that semantically, until the `box` expression returns,
/// the values are still owned by their containing expressions. So
/// we'll see that `&x`.
yield_in_scope: FxHashMap<Scope, (Span, usize)>,

/// The number of visit_expr calls done in the body.
/// Used to sanity check visit_expr call count when
/// The number of visit_expr and visit_pat calls done in the body.
/// Used to sanity check visit_expr/visit_pat call count when
/// calculating geneartor interiors.
body_expr_count: FxHashMap<hir::BodyId, usize>,
}
Expand All @@ -307,8 +345,8 @@ pub struct Context {
struct RegionResolutionVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,

// The number of expressions visited in the current body
expr_count: usize,
// The number of expressions and patterns visited in the current body
expr_and_pat_count: usize,

// Generated scope tree:
scope_tree: ScopeTree,
Expand Down Expand Up @@ -758,6 +796,8 @@ fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &
}

intravisit::walk_pat(visitor, pat);

visitor.expr_and_pat_count += 1;
}

fn resolve_stmt<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
Expand Down Expand Up @@ -863,14 +903,14 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
_ => intravisit::walk_expr(visitor, expr)
}

visitor.expr_count += 1;
visitor.expr_and_pat_count += 1;

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);
loop {
visitor.scope_tree.yield_in_scope.insert(scope,
(expr.span, visitor.expr_count));
(expr.span, visitor.expr_and_pat_count));

// Keep traversing up while we can.
match visitor.scope_tree.parent_map.get(&scope) {
Expand Down Expand Up @@ -1160,7 +1200,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
body_id,
self.cx.parent);

let outer_ec = mem::replace(&mut self.expr_count, 0);
let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0);
let outer_cx = self.cx;
let outer_ts = mem::replace(&mut self.terminating_scopes, FxHashSet());
self.terminating_scopes.insert(body.value.hir_id.local_id);
Expand Down Expand Up @@ -1207,11 +1247,11 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
}

if body.is_generator {
self.scope_tree.body_expr_count.insert(body_id, self.expr_count);
self.scope_tree.body_expr_count.insert(body_id, self.expr_and_pat_count);
}

// Restore context we had at the start.
self.expr_count = outer_ec;
self.expr_and_pat_count = outer_ec;
self.cx = outer_cx;
self.terminating_scopes = outer_ts;
}
Expand Down Expand Up @@ -1246,7 +1286,7 @@ fn region_scope_tree<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
let mut visitor = RegionResolutionVisitor {
tcx,
scope_tree: ScopeTree::default(),
expr_count: 0,
expr_and_pat_count: 0,
cx: Context {
root_id: None,
parent: None,
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_mir/build/expr/as_rvalue.rs
Expand Up @@ -96,7 +96,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
ExprKind::Box { value } => {
let value = this.hir.mirror(value);
let result = this.local_decls.push(LocalDecl::new_temp(expr.ty, expr_span));
// The `Box<T>` temporary created here is not a part of the HIR,
// and therefore is not considered during generator OIBIT
// determination. See the comment about `box` at `yield_in_scope`.
let result = this.local_decls.push(
LocalDecl::new_internal(expr.ty, expr_span));
this.cfg.push(block, Statement {
source_info,
kind: StatementKind::StorageLive(result)
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_typeck/check/generator_interior.rs
Expand Up @@ -41,7 +41,7 @@ impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> {
// be storage-live (and therefore live) at any of the yields.
//
// See the mega-comment at `yield_in_scope` for a proof.
if expr.is_none() || expr_count >= self.expr_count {
if expr_count >= self.expr_count {
Some(span)
} else {
None
Expand Down Expand Up @@ -115,6 +115,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'gcx, 'tcx> {
self.record(ty, Some(scope), None);
}

self.expr_count += 1;

intravisit::walk_pat(self, pat);
}

Expand Down
4 changes: 4 additions & 0 deletions src/test/run-pass/generator/yield-in-args-rev.rs
Expand Up @@ -8,6 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that a borrow that occurs after a yield in the same
// argument list is not treated as live across the yield by
// type-checking.

#![feature(generators)]

fn foo(_a: (), _b: &bool) {}
Expand Down
26 changes: 26 additions & 0 deletions src/test/run-pass/generator/yield-in-box.rs
@@ -0,0 +1,26 @@
// 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.

// Test that box-statements with yields in them work.

#![feature(generators, box_syntax)]

fn main() {
let x = 0i32;
|| {
let y = 2u32;
{
let _t = box (&x, yield 0, &y);
}
match box (&x, yield 0, &y) {
_t => {}
}
};
}

0 comments on commit 018525e

Please sign in to comment.