Skip to content

Commit 1fe7525

Browse files
committed
Rewrite stmt processing not to recurse to avoid stack overflow if there
are tons of let statements. Fixes #29466.
1 parent 6c10d74 commit 1fe7525

File tree

3 files changed

+3673
-43
lines changed

3 files changed

+3673
-43
lines changed

src/librustc_mir/build/scope.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
135135
debug!("in_scope(extent={:?}, block={:?})", extent, block);
136136
self.push_scope(extent, block);
137137
let rv = unpack!(block = f(self));
138-
assert_eq!(self.extent_of_innermost_scope(), extent);
139-
self.pop_scope(block);
138+
self.pop_scope(extent, block);
140139
debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
141140
block.and(rv)
142141
}
@@ -156,13 +155,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
156155
});
157156
}
158157

159-
/// Pops the innermost scope, adding any drops onto the end of
160-
/// `block` that are needed. This must match 1-to-1 with
161-
/// `push_scope`.
162-
pub fn pop_scope(&mut self, block: BasicBlock) {
163-
debug!("pop_scope({:?})", block);
158+
/// Pops a scope, which should have extent `extent`, adding any
159+
/// drops onto the end of `block` that are needed. This must
160+
/// match 1-to-1 with `push_scope`.
161+
pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
162+
debug!("pop_scope({:?}, {:?})", extent, block);
164163
let scope = self.scopes.pop().unwrap();
165164

165+
assert_eq!(scope.extent, extent);
166+
166167
// add in any drops needed on the fallthrough path (any other
167168
// exiting paths, such as those that arise from `break`, will
168169
// have drops already)

src/librustc_mir/build/stmt.rs

+58-36
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,70 @@ use repr::*;
1414

1515
impl<'a,'tcx> Builder<'a,'tcx> {
1616
pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<'tcx>>) -> BlockAnd<()> {
17-
for stmt in stmts {
18-
unpack!(block = self.stmt(block, stmt));
19-
}
20-
block.unit()
21-
}
22-
23-
pub fn stmt(&mut self, mut block: BasicBlock, stmt: StmtRef<'tcx>) -> BlockAnd<()> {
17+
// This convoluted structure is to avoid using recursion as we walk down a list
18+
// of statements. Basically, the structure we get back is something like:
19+
//
20+
// let x = <init> in {
21+
// let y = <init> in {
22+
// expr1;
23+
// expr2;
24+
// }
25+
// }
26+
//
27+
// To process this, we keep a stack of (Option<CodeExtent>,
28+
// vec::IntoIter<Stmt>) pairs. At each point we pull off the
29+
// top most pair and extract one statement from the
30+
// iterator. Once it's complete, we pop the scope from the
31+
// first half the pair.
2432
let this = self;
25-
let Stmt { span, kind } = this.hir.mirror(stmt);
26-
match kind {
27-
StmtKind::Let { remainder_scope,
28-
init_scope,
29-
pattern,
30-
initializer: Some(initializer),
31-
stmts } => {
32-
this.in_scope(remainder_scope, block, |this| {
33-
unpack!(block = this.in_scope(init_scope, block, |this| {
34-
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
33+
let mut stmt_lists = vec![(None, stmts.into_iter())];
34+
while !stmt_lists.is_empty() {
35+
let stmt = {
36+
let &mut (_, ref mut stmts) = stmt_lists.last_mut().unwrap();
37+
stmts.next()
38+
};
39+
40+
let stmt = match stmt {
41+
Some(stmt) => stmt,
42+
None => {
43+
let (extent, _) = stmt_lists.pop().unwrap();
44+
if let Some(extent) = extent {
45+
this.pop_scope(extent, block);
46+
}
47+
continue
48+
}
49+
};
50+
51+
let Stmt { span, kind } = this.hir.mirror(stmt);
52+
match kind {
53+
StmtKind::Let { remainder_scope, init_scope, pattern, initializer, stmts } => {
54+
this.push_scope(remainder_scope, block);
55+
stmt_lists.push((Some(remainder_scope), stmts.into_iter()));
56+
unpack!(block = this.in_scope(init_scope, block, move |this| {
57+
// FIXME #30046 ^~~~
58+
match initializer {
59+
Some(initializer) => {
60+
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
61+
}
62+
None => {
63+
this.declare_bindings(remainder_scope, &pattern);
64+
block.unit()
65+
}
66+
}
3567
}));
36-
this.stmts(block, stmts)
37-
})
38-
}
68+
}
3969

40-
StmtKind::Let { remainder_scope, init_scope, pattern, initializer: None, stmts } => {
41-
this.in_scope(remainder_scope, block, |this| {
42-
unpack!(block = this.in_scope(init_scope, block, |this| {
43-
this.declare_bindings(remainder_scope, &pattern);
70+
StmtKind::Expr { scope, expr } => {
71+
unpack!(block = this.in_scope(scope, block, |this| {
72+
let expr = this.hir.mirror(expr);
73+
let temp = this.temp(expr.ty.clone());
74+
unpack!(block = this.into(&temp, block, expr));
75+
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
4476
block.unit()
4577
}));
46-
this.stmts(block, stmts)
47-
})
48-
}
49-
50-
StmtKind::Expr { scope, expr } => {
51-
this.in_scope(scope, block, |this| {
52-
let expr = this.hir.mirror(expr);
53-
let temp = this.temp(expr.ty.clone());
54-
unpack!(block = this.into(&temp, block, expr));
55-
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
56-
block.unit()
57-
})
78+
}
5879
}
5980
}
81+
block.unit()
6082
}
6183
}

0 commit comments

Comments
 (0)