Make list of statements flat

In MIR we previously tried to match `let x in { exprs; let y in { exprs; }}` with our data
structures which is rather unwieldy, espeicially because it requires some sort of recursion or
stack to process, while, a flat list of statements is enough – lets only relinquish their lifetime
at the end of the block (i.e. end of the list).

Also fixes #31853.
This commit is contained in:
Simonas Kazlauskas 2016-02-24 17:14:03 +02:00
parent fbd8171155
commit 77be4ecc17
7 changed files with 73 additions and 110 deletions

View file

@ -198,8 +198,8 @@ pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &ty::ctxt<'tcx>,
fn visit_item(&mut self, i: &'tcx hir::Item) {
let item_def_id = self.tcx.map.local_def_id(i.id);
let task_id = (self.dep_node_fn)(item_def_id);
debug!("About to start task {:?}", task_id);
let _task = self.tcx.dep_graph.in_task(task_id);
debug!("Started task {:?}", task_id);
self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
self.visitor.visit_item(i)
}

View file

@ -21,14 +21,70 @@ impl<'a,'tcx> Builder<'a,'tcx> {
-> BlockAnd<()> {
let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
self.in_scope(extent, block, move |this| {
unpack!(block = this.stmts(block, stmts));
match expr {
Some(expr) => this.into(destination, block, expr),
None => {
this.cfg.push_assign_unit(block, span, destination);
block.unit()
// This convoluted structure is to avoid using recursion as we walk down a list
// of statements. Basically, the structure we get back is something like:
//
// let x = <init> in {
// expr1;
// let y = <init> in {
// expr2;
// expr3;
// ...
// }
// }
//
// The let bindings are valid till the end of block so all we have to do is to pop all
// the let-scopes at the end.
//
// First we build all the statements in the block.
let mut let_extent_stack = Vec::with_capacity(8);
for stmt in stmts {
let Stmt { span: _, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this| {
let expr = this.hir.mirror(expr);
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
unpack!(block = this.build_drop(block, temp));
block.unit()
}));
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
this.push_scope(remainder_scope);
let_extent_stack.push(remainder_scope);
unpack!(block = this.in_scope(init_scope, block, move |this| {
// FIXME #30046 ^~~~
match initializer {
Some(initializer) => {
this.expr_into_pattern(block,
remainder_scope,
pattern,
initializer)
}
None => {
this.declare_bindings(remainder_scope, &pattern);
block.unit()
}
}
}));
}
}
}
// Then, the block may have an optional trailing expression which is a “return” value
// of the block.
if let Some(expr) = expr {
unpack!(block = this.into(destination, block, expr));
} else {
// FIXME(#31472)
this.cfg.push_assign_unit(block, span, destination);
}
// Finally, we pop all the let scopes before exiting out from the scope of block
// itself.
for extent in let_extent_stack.into_iter().rev() {
unpack!(block = this.pop_scope(extent, block));
}
block.unit()
})
}
}

View file

@ -185,4 +185,3 @@ mod into;
mod matches;
mod misc;
mod scope;
mod stmt;

View file

@ -249,6 +249,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
extent: CodeExtent,
mut block: BasicBlock,
target: BasicBlock) {
debug!("exit_scope(extent={:?}, block={:?}, target={:?})", extent, block, target);
let scope_count = 1 + self.scopes.iter().rev().position(|scope| scope.extent == extent)
.unwrap_or_else(||{
self.hir.span_bug(span, &format!("extent {:?} does not enclose", extent))

View file

@ -1,83 +0,0 @@
// Copyright 2015 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.
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*;
use rustc::mir::repr::*;
impl<'a,'tcx> Builder<'a,'tcx> {
pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<'tcx>>) -> BlockAnd<()> {
// This convoluted structure is to avoid using recursion as we walk down a list
// of statements. Basically, the structure we get back is something like:
//
// let x = <init> in {
// let y = <init> in {
// expr1;
// expr2;
// }
// }
//
// To process this, we keep a stack of (Option<CodeExtent>,
// vec::IntoIter<Stmt>) pairs. At each point we pull off the
// top most pair and extract one statement from the
// iterator. Once it's complete, we pop the scope from the
// first half the pair.
let this = self;
let mut stmt_lists = vec![(None, stmts.into_iter())];
while !stmt_lists.is_empty() {
let stmt = {
let &mut (_, ref mut stmts) = stmt_lists.last_mut().unwrap();
stmts.next()
};
let stmt = match stmt {
Some(stmt) => stmt,
None => {
let (extent, _) = stmt_lists.pop().unwrap();
if let Some(extent) = extent {
unpack!(block = this.pop_scope(extent, block));
}
continue
}
};
let Stmt { span: _, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Let { remainder_scope, init_scope, pattern, initializer, stmts } => {
this.push_scope(remainder_scope);
stmt_lists.push((Some(remainder_scope), stmts.into_iter()));
unpack!(block = this.in_scope(init_scope, block, move |this| {
// FIXME #30046 ^~~~
match initializer {
Some(initializer) => {
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
}
None => {
this.declare_bindings(remainder_scope, &pattern);
block.unit()
}
}
}));
}
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this| {
let expr = this.hir.mirror(expr);
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
unpack!(block = this.build_drop(block, temp));
block.unit()
}));
}
}
}
block.unit()
}
}

View file

@ -26,7 +26,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
extent: cx.tcx.region_maps.node_extent(self.id),
span: self.span,
stmts: stmts,
expr: self.expr.to_ref(),
expr: self.expr.to_ref()
}
}
}
@ -41,13 +41,13 @@ fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>,
while let Some((index, stmt)) = stmts.next() {
match stmt.node {
hir::StmtExpr(ref expr, id) | hir::StmtSemi(ref expr, id) =>
result.push(
StmtRef::Mirror(
Box::new(Stmt { span: stmt.span,
kind: StmtKind::Expr {
scope: cx.tcx.region_maps.node_extent(id),
expr: expr.to_ref() } }))),
result.push(StmtRef::Mirror(Box::new(Stmt {
span: stmt.span,
kind: StmtKind::Expr {
scope: cx.tcx.region_maps.node_extent(id),
expr: expr.to_ref()
}
}))),
hir::StmtDecl(ref decl, id) => {
match decl.node {
hir::DeclItem(..) => { /* ignore for purposes of the MIR */ }
@ -59,10 +59,6 @@ fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>,
let remainder_extent =
cx.tcx.region_maps.lookup_code_extent(remainder_extent);
// pull in all following statements, since
// they are within the scope of this let:
let following_stmts = mirror_stmts(cx, block_id, stmts);
let pattern = cx.irrefutable_pat(&local.pat);
result.push(StmtRef::Mirror(Box::new(Stmt {
span: stmt.span,
@ -71,11 +67,8 @@ fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>,
init_scope: cx.tcx.region_maps.node_extent(id),
pattern: pattern,
initializer: local.init.to_ref(),
stmts: following_stmts,
},
})));
return result;
}
}
}

View file

@ -78,10 +78,7 @@ pub enum StmtKind<'tcx> {
pattern: Pattern<'tcx>,
/// let pat = <INIT> ...
initializer: Option<ExprRef<'tcx>>,
/// let pat = init; <STMTS>
stmts: Vec<StmtRef<'tcx>>,
initializer: Option<ExprRef<'tcx>>
},
}