Skip to content

Commit a0fba3e

Browse files
arielb1nikomatsakis
authored andcommitted
fix promotion of MIR terminators
promotion of MIR terminators used to try to promote the destination it is trying to promote, leading to stack overflow. Fixes rust-lang#37991.
1 parent 5196567 commit a0fba3e

File tree

3 files changed

+87
-79
lines changed

3 files changed

+87
-79
lines changed

Diff for: src/librustc_mir/transform/promote_consts.rs

+66-78
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl TempState {
6666
/// A "root candidate" for promotion, which will become the
6767
/// returned value in a promoted MIR, unless it's a subset
6868
/// of a larger candidate.
69+
#[derive(Debug)]
6970
pub enum Candidate {
7071
/// Borrow of a constant temporary.
7172
Ref(Location),
@@ -190,15 +191,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
190191
/// promoted MIR, recursing through temps.
191192
fn promote_temp(&mut self, temp: Local) -> Local {
192193
let old_keep_original = self.keep_original;
193-
let (bb, stmt_idx) = match self.temps[temp] {
194-
TempState::Defined {
195-
location: Location { block, statement_index },
196-
uses
197-
} if uses > 0 => {
194+
let loc = match self.temps[temp] {
195+
TempState::Defined { location, uses } if uses > 0 => {
198196
if uses > 1 {
199197
self.keep_original = true;
200198
}
201-
(block, statement_index)
199+
location
202200
}
203201
state => {
204202
span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
@@ -209,91 +207,80 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
209207
self.temps[temp] = TempState::PromotedOut;
210208
}
211209

212-
let no_stmts = self.source[bb].statements.len();
210+
let no_stmts = self.source[loc.block].statements.len();
211+
let new_temp = self.promoted.local_decls.push(
212+
LocalDecl::new_temp(self.source.local_decls[temp].ty));
213+
214+
debug!("promote({:?} @ {:?}/{:?}, {:?})",
215+
temp, loc, no_stmts, self.keep_original);
213216

214217
// First, take the Rvalue or Call out of the source MIR,
215218
// or duplicate it, depending on keep_original.
216-
let (mut rvalue, mut call) = (None, None);
217-
let source_info = if stmt_idx < no_stmts {
218-
let statement = &mut self.source[bb].statements[stmt_idx];
219-
let rhs = match statement.kind {
220-
StatementKind::Assign(_, ref mut rhs) => rhs,
221-
_ => {
222-
span_bug!(statement.source_info.span, "{:?} is not an assignment",
223-
statement);
224-
}
219+
if loc.statement_index < no_stmts {
220+
let (mut rvalue, source_info) = {
221+
let statement = &mut self.source[loc.block].statements[loc.statement_index];
222+
let rhs = match statement.kind {
223+
StatementKind::Assign(_, ref mut rhs) => rhs,
224+
_ => {
225+
span_bug!(statement.source_info.span, "{:?} is not an assignment",
226+
statement);
227+
}
228+
};
229+
230+
(if self.keep_original {
231+
rhs.clone()
232+
} else {
233+
let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
234+
mem::replace(rhs, unit)
235+
}, statement.source_info)
225236
};
226-
if self.keep_original {
227-
rvalue = Some(rhs.clone());
228-
} else {
229-
let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
230-
rvalue = Some(mem::replace(rhs, unit));
231-
}
232-
statement.source_info
233-
} else if self.keep_original {
234-
let terminator = self.source[bb].terminator().clone();
235-
call = Some(terminator.kind);
236-
terminator.source_info
237+
238+
self.visit_rvalue(&mut rvalue, loc);
239+
self.assign(new_temp, rvalue, source_info.span);
237240
} else {
238-
let terminator = self.source[bb].terminator_mut();
239-
let target = match terminator.kind {
240-
TerminatorKind::Call {
241-
destination: ref mut dest @ Some(_),
242-
ref mut cleanup, ..
243-
} => {
244-
// No cleanup necessary.
245-
cleanup.take();
246-
247-
// We'll put a new destination in later.
248-
dest.take().unwrap().1
249-
}
250-
ref kind => {
251-
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
241+
let mut terminator = if self.keep_original {
242+
self.source[loc.block].terminator().clone()
243+
} else {
244+
let terminator = self.source[loc.block].terminator_mut();
245+
let target = match terminator.kind {
246+
TerminatorKind::Call { destination: Some((_, target)), .. } => target,
247+
ref kind => {
248+
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
249+
}
250+
};
251+
Terminator {
252+
source_info: terminator.source_info,
253+
kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto {
254+
target: target
255+
})
252256
}
253257
};
254-
call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
255-
target: target
256-
}));
257-
terminator.source_info
258-
};
259258

260-
// Then, recurse for components in the Rvalue or Call.
261-
if stmt_idx < no_stmts {
262-
self.visit_rvalue(rvalue.as_mut().unwrap(), Location {
263-
block: bb,
264-
statement_index: stmt_idx
265-
});
266-
} else {
267-
self.visit_terminator_kind(bb, call.as_mut().unwrap(), Location {
268-
block: bb,
269-
statement_index: no_stmts
270-
});
271-
}
272-
273-
let new_temp = self.promoted.local_decls.push(
274-
LocalDecl::new_temp(self.source.local_decls[temp].ty));
275-
276-
// Inject the Rvalue or Call into the promoted MIR.
277-
if stmt_idx < no_stmts {
278-
self.assign(new_temp, rvalue.unwrap(), source_info.span);
279-
} else {
280259
let last = self.promoted.basic_blocks().last().unwrap();
281260
let new_target = self.new_block();
282-
let mut call = call.unwrap();
283-
match call {
284-
TerminatorKind::Call { ref mut destination, ..} => {
285-
*destination = Some((Lvalue::Local(new_temp), new_target));
261+
262+
terminator.kind = match terminator.kind {
263+
TerminatorKind::Call { mut func, mut args, .. } => {
264+
self.visit_operand(&mut func, loc);
265+
for arg in &mut args {
266+
self.visit_operand(arg, loc);
267+
}
268+
TerminatorKind::Call {
269+
func: func,
270+
args: args,
271+
cleanup: None,
272+
destination: Some((Lvalue::Local(new_temp), new_target))
273+
}
286274
}
287-
_ => bug!()
288-
}
289-
let terminator = self.promoted[last].terminator_mut();
290-
terminator.source_info.span = source_info.span;
291-
terminator.kind = call;
292-
}
275+
ref kind => {
276+
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
277+
}
278+
};
293279

294-
// Restore the old duplication state.
295-
self.keep_original = old_keep_original;
280+
*self.promoted[last].terminator_mut() = terminator;
281+
};
296282

283+
self.keep_original = old_keep_original;
297284
new_temp
298285
}
299286

@@ -355,6 +342,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
355342
mut temps: IndexVec<Local, TempState>,
356343
candidates: Vec<Candidate>) {
357344
// Visit candidates in reverse, in case they're nested.
345+
debug!("promote_candidates({:?})", candidates);
358346
for candidate in candidates.into_iter().rev() {
359347
let (span, ty) = match candidate {
360348
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {

Diff for: src/librustc_mir/transform/qualify_consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -993,9 +993,9 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants {
993993
Entry::Vacant(entry) => {
994994
// Guard against `const` recursion.
995995
entry.insert(Qualif::RECURSIVE);
996+
Mode::Const
996997
}
997998
}
998-
Mode::Const
999999
}
10001000
MirSource::Static(_, hir::MutImmutable) => Mode::Static,
10011001
MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,

Diff for: src/test/run-pass/issue-37991.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(const_fn)]
12+
13+
const fn foo() -> i64 {
14+
3
15+
}
16+
17+
fn main() {
18+
let val = &(foo() % 2);
19+
assert_eq!(*val, 1);
20+
}

0 commit comments

Comments
 (0)