Skip to content

Commit b9af400

Browse files
committed
rustc_mir: generate an extra temporary during borrowed rvalue promotion.
1 parent 3ec2058 commit b9af400

File tree

2 files changed

+98
-60
lines changed

2 files changed

+98
-60
lines changed

src/librustc_mir/transform/promote_consts.rs

Lines changed: 92 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,12 @@
2525
use rustc::mir::*;
2626
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
2727
use rustc::mir::traversal::ReversePostorder;
28-
use rustc::ty::TyCtxt;
28+
use rustc::ty::{self, TyCtxt};
2929
use syntax_pos::Span;
3030

3131
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
3232

33-
use std::iter;
34-
use std::mem;
35-
use std::usize;
33+
use std::{cmp, iter, mem, usize};
3634

3735
/// State of a temporary during collection and promotion.
3836
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -150,9 +148,11 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Local, T
150148
}
151149

152150
struct Promoter<'a, 'tcx: 'a> {
151+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
153152
source: &'a mut Mir<'tcx>,
154153
promoted: Mir<'tcx>,
155154
temps: &'a mut IndexVec<Local, TempState>,
155+
extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
156156

157157
/// If true, all nested temps are also kept in the
158158
/// source MIR, not moved to the promoted MIR.
@@ -288,38 +288,78 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
288288
}
289289

290290
fn promote_candidate(mut self, candidate: Candidate) {
291-
let span = self.promoted.span;
292-
let new_operand = Operand::Constant(box Constant {
293-
span,
294-
ty: self.promoted.return_ty(),
295-
literal: Literal::Promoted {
291+
let mut rvalue = {
292+
let promoted = &mut self.promoted;
293+
let literal = Literal::Promoted {
296294
index: Promoted::new(self.source.promoted.len())
297-
}
298-
});
299-
let mut rvalue = match candidate {
300-
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
301-
let ref mut statement = self.source[bb].statements[stmt_idx];
302-
match statement.kind {
303-
StatementKind::Assign(_, ref mut rvalue) => {
304-
mem::replace(rvalue, Rvalue::Use(new_operand))
295+
};
296+
let operand = |ty, span| {
297+
promoted.span = span;
298+
promoted.local_decls[RETURN_PLACE] =
299+
LocalDecl::new_return_place(ty, span);
300+
Operand::Constant(box Constant {
301+
span,
302+
ty,
303+
literal
304+
})
305+
};
306+
let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
307+
match candidate {
308+
Candidate::Ref(loc) => {
309+
let ref mut statement = blocks[loc.block].statements[loc.statement_index];
310+
match statement.kind {
311+
StatementKind::Assign(_, Rvalue::Ref(r, bk, ref mut place)) => {
312+
let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx);
313+
let ref_ty = self.tcx.mk_ref(r,
314+
ty::TypeAndMut {
315+
ty,
316+
mutbl: bk.to_mutbl_lossy()
317+
}
318+
);
319+
let span = statement.source_info.span;
320+
321+
// Create a temp to hold the promoted reference.
322+
// This is because `*r` requires `r` to be a local,
323+
// otherwise we would use the `promoted` directly.
324+
let mut promoted_ref = LocalDecl::new_temp(ref_ty, span);
325+
promoted_ref.source_info = statement.source_info;
326+
let promoted_ref = local_decls.push(promoted_ref);
327+
assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
328+
self.extra_statements.push((loc, Statement {
329+
source_info: statement.source_info,
330+
kind: StatementKind::Assign(
331+
Place::Local(promoted_ref),
332+
Rvalue::Use(operand(ref_ty, span)),
333+
)
334+
}));
335+
let promoted_place = Place::Local(promoted_ref).deref();
336+
337+
Rvalue::Ref(r, bk, mem::replace(place, promoted_place))
338+
}
339+
_ => bug!()
305340
}
306-
_ => bug!()
307341
}
308-
}
309-
Candidate::Argument { bb, index } => {
310-
match self.source[bb].terminator_mut().kind {
311-
TerminatorKind::Call { ref mut args, .. } => {
312-
Rvalue::Use(mem::replace(&mut args[index], new_operand))
342+
Candidate::Argument { bb, index } => {
343+
let terminator = blocks[bb].terminator_mut();
344+
match terminator.kind {
345+
TerminatorKind::Call { ref mut args, .. } => {
346+
let ty = args[index].ty(local_decls, self.tcx);
347+
let span = terminator.source_info.span;
348+
Rvalue::Use(mem::replace(&mut args[index], operand(ty, span)))
349+
}
350+
_ => bug!()
313351
}
314-
_ => bug!()
315352
}
316353
}
317354
};
355+
356+
assert_eq!(self.new_block(), START_BLOCK);
318357
self.visit_rvalue(&mut rvalue, Location {
319358
block: BasicBlock::new(0),
320359
statement_index: usize::MAX
321360
});
322361

362+
let span = self.promoted.span;
323363
self.assign(RETURN_PLACE, rvalue, span);
324364
self.source.promoted.push(self.promoted);
325365
}
@@ -343,43 +383,29 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
343383
candidates: Vec<Candidate>) {
344384
// Visit candidates in reverse, in case they're nested.
345385
debug!("promote_candidates({:?})", candidates);
386+
387+
let mut extra_statements = vec![];
346388
for candidate in candidates.into_iter().rev() {
347-
let (span, ty) = match candidate {
348-
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
349-
let statement = &mir[bb].statements[stmt_idx];
350-
let dest = match statement.kind {
351-
StatementKind::Assign(ref dest, _) => dest,
352-
_ => {
353-
span_bug!(statement.source_info.span,
354-
"expected assignment to promote");
355-
}
356-
};
357-
if let Place::Local(index) = *dest {
358-
if temps[index] == TempState::PromotedOut {
359-
// Already promoted.
360-
continue;
389+
match candidate {
390+
Candidate::Ref(Location { block, statement_index }) => {
391+
match mir[block].statements[statement_index].kind {
392+
StatementKind::Assign(Place::Local(local), _) => {
393+
if temps[local] == TempState::PromotedOut {
394+
// Already promoted.
395+
continue;
396+
}
361397
}
398+
_ => {}
362399
}
363-
(statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
364-
}
365-
Candidate::Argument { bb, index } => {
366-
let terminator = mir[bb].terminator();
367-
let ty = match terminator.kind {
368-
TerminatorKind::Call { ref args, .. } => {
369-
args[index].ty(mir, tcx)
370-
}
371-
_ => {
372-
span_bug!(terminator.source_info.span,
373-
"expected call argument to promote");
374-
}
375-
};
376-
(terminator.source_info.span, ty)
377400
}
378-
};
401+
Candidate::Argument { .. } => {}
402+
}
403+
379404

380-
// Declare return place local
381-
let initial_locals = iter::once(LocalDecl::new_return_place(ty, span))
382-
.collect();
405+
// Declare return place local so that `Mir::new` doesn't complain.
406+
let initial_locals = iter::once(
407+
LocalDecl::new_return_place(tcx.types.never, mir.span)
408+
).collect();
383409

384410
let mut promoter = Promoter {
385411
promoted: Mir::new(
@@ -393,16 +419,24 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
393419
initial_locals,
394420
0,
395421
vec![],
396-
span
422+
mir.span
397423
),
424+
tcx,
398425
source: mir,
399426
temps: &mut temps,
427+
extra_statements: &mut extra_statements,
400428
keep_original: false
401429
};
402-
assert_eq!(promoter.new_block(), START_BLOCK);
403430
promoter.promote_candidate(candidate);
404431
}
405432

433+
// Insert each of `extra_statements` before its indicated location, which
434+
// has to be done in reverse location order, to not invalidate the rest.
435+
extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc));
436+
for (loc, statement) in extra_statements {
437+
mir[loc.block].statements.insert(loc.statement_index, statement);
438+
}
439+
406440
// Eliminate assignments to, and drops of promoted temps.
407441
let promoted = |index: Local| temps[index] == TempState::PromotedOut;
408442
for block in mir.basic_blocks_mut() {

src/test/mir-opt/end_region_destruction_extents_1.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,17 +130,21 @@ unsafe impl<'a, #[may_dangle] 'b> Drop for D1<'a, 'b> {
130130
// let mut _7: &'10s S1;
131131
// let mut _8: &'10s S1;
132132
// let mut _9: S1;
133+
// let mut _10: &'10s S1;
134+
// let mut _11: &'12ds S1;
133135
//
134136
// bb0: {
135137
// StorageLive(_2);
136138
// StorageLive(_3);
137139
// StorageLive(_4);
138140
// StorageLive(_5);
139-
// _5 = promoted[1];
141+
// _11 = promoted[1];
142+
// _5 = &'12ds (*_11);
140143
// _4 = &'12ds (*_5);
141144
// StorageLive(_7);
142145
// StorageLive(_8);
143-
// _8 = promoted[0];
146+
// _10 = promoted[0];
147+
// _8 = &'10s (*_10);
144148
// _7 = &'10s (*_8);
145149
// _3 = D1<'12ds, '10s>::{{constructor}}(move _4, move _7);
146150
// EndRegion('10s);

0 commit comments

Comments
 (0)