Rewrite stmt processing not to recurse to avoid stack overflow if there
are tons of let statements. Fixes #29466.
This commit is contained in:
parent
6c10d74a32
commit
1fe7525180
@ -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)
|
||||
|
@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3607
src/test/run-pass/issue-29466.rs
Normal file
3607
src/test/run-pass/issue-29466.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user