Rewrite stmt processing not to recurse to avoid stack overflow if there

are tons of let statements. Fixes #29466.
This commit is contained in:
Niko Matsakis 2015-11-24 17:24:21 -05:00
parent 6c10d74a32
commit 1fe7525180
3 changed files with 3678 additions and 48 deletions

View File

@ -135,8 +135,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
debug!("in_scope(extent={:?}, block={:?})", extent, block);
self.push_scope(extent, block);
let rv = unpack!(block = f(self));
assert_eq!(self.extent_of_innermost_scope(), extent);
self.pop_scope(block);
self.pop_scope(extent, block);
debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
block.and(rv)
}
@ -156,13 +155,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
});
}
/// Pops the innermost scope, adding any drops onto the end of
/// `block` that are needed. This must match 1-to-1 with
/// `push_scope`.
pub fn pop_scope(&mut self, block: BasicBlock) {
debug!("pop_scope({:?})", block);
/// Pops a scope, which should have extent `extent`, adding any
/// drops onto the end of `block` that are needed. This must
/// match 1-to-1 with `push_scope`.
pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
debug!("pop_scope({:?}, {:?})", extent, block);
let scope = self.scopes.pop().unwrap();
assert_eq!(scope.extent, extent);
// add in any drops needed on the fallthrough path (any other
// exiting paths, such as those that arise from `break`, will
// have drops already)

View File

@ -14,48 +14,70 @@ use repr::*;
impl<'a,'tcx> Builder<'a,'tcx> {
pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<'tcx>>) -> BlockAnd<()> {
for stmt in stmts {
unpack!(block = self.stmt(block, stmt));
// 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 {
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, block);
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));
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
block.unit()
}));
}
}
}
block.unit()
}
pub fn stmt(&mut self, mut block: BasicBlock, stmt: StmtRef<'tcx>) -> BlockAnd<()> {
let this = self;
let Stmt { span, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Let { remainder_scope,
init_scope,
pattern,
initializer: Some(initializer),
stmts } => {
this.in_scope(remainder_scope, block, |this| {
unpack!(block = this.in_scope(init_scope, block, |this| {
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
}));
this.stmts(block, stmts)
})
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer: None, stmts } => {
this.in_scope(remainder_scope, block, |this| {
unpack!(block = this.in_scope(init_scope, block, |this| {
this.declare_bindings(remainder_scope, &pattern);
block.unit()
}));
this.stmts(block, stmts)
})
}
StmtKind::Expr { scope, expr } => {
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));
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
block.unit()
})
}
}
}
}

File diff suppressed because it is too large Load Diff