Skip to content

Commit c6a1979

Browse files
committed
Auto merge of rust-lang#50603 - eddyb:issue-49955, r=nikomatsakis
rustc_mir: allow promotion of promotable temps indexed at runtime. Fixes rust-lang#49955. r? @nikomatsakis
2 parents 37a4091 + d1f117d commit c6a1979

File tree

6 files changed

+217
-82
lines changed

6 files changed

+217
-82
lines changed

src/librustc_mir/transform/promote_consts.rs

+104-58
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,90 @@ 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+
// Find the underlying local for this (necessarilly interior) borrow.
313+
// HACK(eddyb) using a recursive function because of mutable borrows.
314+
fn interior_base<'a, 'tcx>(place: &'a mut Place<'tcx>)
315+
-> &'a mut Place<'tcx> {
316+
if let Place::Projection(ref mut proj) = *place {
317+
assert_ne!(proj.elem, ProjectionElem::Deref);
318+
return interior_base(&mut proj.base);
319+
}
320+
place
321+
}
322+
let place = interior_base(place);
323+
324+
let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx);
325+
let ref_ty = self.tcx.mk_ref(r,
326+
ty::TypeAndMut {
327+
ty,
328+
mutbl: bk.to_mutbl_lossy()
329+
}
330+
);
331+
let span = statement.source_info.span;
332+
333+
// Create a temp to hold the promoted reference.
334+
// This is because `*r` requires `r` to be a local,
335+
// otherwise we would use the `promoted` directly.
336+
let mut promoted_ref = LocalDecl::new_temp(ref_ty, span);
337+
promoted_ref.source_info = statement.source_info;
338+
let promoted_ref = local_decls.push(promoted_ref);
339+
assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
340+
self.extra_statements.push((loc, Statement {
341+
source_info: statement.source_info,
342+
kind: StatementKind::Assign(
343+
Place::Local(promoted_ref),
344+
Rvalue::Use(operand(ref_ty, span)),
345+
)
346+
}));
347+
let promoted_place = Place::Local(promoted_ref).deref();
348+
349+
Rvalue::Ref(r, bk, mem::replace(place, promoted_place))
350+
}
351+
_ => bug!()
305352
}
306-
_ => bug!()
307353
}
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))
354+
Candidate::Argument { bb, index } => {
355+
let terminator = blocks[bb].terminator_mut();
356+
match terminator.kind {
357+
TerminatorKind::Call { ref mut args, .. } => {
358+
let ty = args[index].ty(local_decls, self.tcx);
359+
let span = terminator.source_info.span;
360+
Rvalue::Use(mem::replace(&mut args[index], operand(ty, span)))
361+
}
362+
_ => bug!()
313363
}
314-
_ => bug!()
315364
}
316365
}
317366
};
367+
368+
assert_eq!(self.new_block(), START_BLOCK);
318369
self.visit_rvalue(&mut rvalue, Location {
319370
block: BasicBlock::new(0),
320371
statement_index: usize::MAX
321372
});
322373

374+
let span = self.promoted.span;
323375
self.assign(RETURN_PLACE, rvalue, span);
324376
self.source.promoted.push(self.promoted);
325377
}
@@ -343,43 +395,29 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
343395
candidates: Vec<Candidate>) {
344396
// Visit candidates in reverse, in case they're nested.
345397
debug!("promote_candidates({:?})", candidates);
398+
399+
let mut extra_statements = vec![];
346400
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;
401+
match candidate {
402+
Candidate::Ref(Location { block, statement_index }) => {
403+
match mir[block].statements[statement_index].kind {
404+
StatementKind::Assign(Place::Local(local), _) => {
405+
if temps[local] == TempState::PromotedOut {
406+
// Already promoted.
407+
continue;
408+
}
361409
}
410+
_ => {}
362411
}
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)
377412
}
378-
};
413+
Candidate::Argument { .. } => {}
414+
}
415+
379416

380-
// Declare return place local
381-
let initial_locals = iter::once(LocalDecl::new_return_place(ty, span))
382-
.collect();
417+
// Declare return place local so that `Mir::new` doesn't complain.
418+
let initial_locals = iter::once(
419+
LocalDecl::new_return_place(tcx.types.never, mir.span)
420+
).collect();
383421

384422
let mut promoter = Promoter {
385423
promoted: Mir::new(
@@ -393,16 +431,24 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
393431
initial_locals,
394432
0,
395433
vec![],
396-
span
434+
mir.span
397435
),
436+
tcx,
398437
source: mir,
399438
temps: &mut temps,
439+
extra_statements: &mut extra_statements,
400440
keep_original: false
401441
};
402-
assert_eq!(promoter.new_block(), START_BLOCK);
403442
promoter.promote_candidate(candidate);
404443
}
405444

445+
// Insert each of `extra_statements` before its indicated location, which
446+
// has to be done in reverse location order, to not invalidate the rest.
447+
extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc));
448+
for (loc, statement) in extra_statements {
449+
mir[loc.block].statements.insert(loc.statement_index, statement);
450+
}
451+
406452
// Eliminate assignments to, and drops of promoted temps.
407453
let promoted = |index: Local| temps[index] == TempState::PromotedOut;
408454
for block in mir.basic_blocks_mut() {

src/librustc_mir/transform/qualify_consts.rs

+49-21
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,12 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
229229
}
230230

231231
/// Check if a Local with the current qualifications is promotable.
232-
fn can_promote(&mut self) -> bool {
232+
fn can_promote(&self, qualif: Qualif) -> bool {
233233
// References to statics are allowed, but only in other statics.
234234
if self.mode == Mode::Static || self.mode == Mode::StaticMut {
235-
(self.qualif - Qualif::STATIC_REF).is_empty()
235+
(qualif - Qualif::STATIC_REF).is_empty()
236236
} else {
237-
self.qualif.is_empty()
237+
qualif.is_empty()
238238
}
239239
}
240240

@@ -679,24 +679,31 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
679679
}
680680

681681
let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
682+
683+
// Default to forbidding the borrow and/or its promotion,
684+
// due to the potential for direct or interior mutability,
685+
// and only proceed by setting `forbidden_mut` to `false`.
686+
let mut forbidden_mut = true;
687+
682688
if let BorrowKind::Mut { .. } = kind {
683689
// In theory, any zero-sized value could be borrowed
684690
// mutably without consequences. However, only &mut []
685691
// is allowed right now, and only in functions.
686-
let allow = if self.mode == Mode::StaticMut {
692+
if self.mode == Mode::StaticMut {
687693
// Inside a `static mut`, &mut [...] is also allowed.
688694
match ty.sty {
689-
ty::TyArray(..) | ty::TySlice(_) => true,
690-
_ => false
695+
ty::TyArray(..) | ty::TySlice(_) => forbidden_mut = false,
696+
_ => {}
691697
}
692698
} else if let ty::TyArray(_, len) = ty.sty {
693-
len.unwrap_usize(self.tcx) == 0 &&
694-
self.mode == Mode::Fn
695-
} else {
696-
false
697-
};
699+
// FIXME(eddyb) the `self.mode == Mode::Fn` condition
700+
// seems unnecessary, given that this is merely a ZST.
701+
if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
702+
forbidden_mut = false;
703+
}
704+
}
698705

699-
if !allow {
706+
if forbidden_mut {
700707
self.add(Qualif::NOT_CONST);
701708
if self.mode != Mode::Fn {
702709
let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
@@ -722,25 +729,46 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
722729
// it means that our "silent insertion of statics" could change
723730
// initializer values (very bad).
724731
if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
725-
// Replace MUTABLE_INTERIOR with NOT_CONST to avoid
732+
// A reference of a MUTABLE_INTERIOR place is instead
733+
// NOT_CONST (see `if forbidden_mut` below), to avoid
726734
// duplicate errors (from reborrowing, for example).
727735
self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
728-
self.add(Qualif::NOT_CONST);
729736
if self.mode != Mode::Fn {
730737
span_err!(self.tcx.sess, self.span, E0492,
731738
"cannot borrow a constant which may contain \
732739
interior mutability, create a static instead");
733740
}
741+
} else {
742+
// We allow immutable borrows of frozen data.
743+
forbidden_mut = false;
734744
}
735745
}
736746

737-
// We might have a candidate for promotion.
738-
let candidate = Candidate::Ref(location);
739-
if self.can_promote() {
740-
// We can only promote direct borrows of temps.
747+
if forbidden_mut {
748+
self.add(Qualif::NOT_CONST);
749+
} else {
750+
// We might have a candidate for promotion.
751+
let candidate = Candidate::Ref(location);
752+
// We can only promote interior borrows of promotable temps.
753+
let mut place = place;
754+
while let Place::Projection(ref proj) = *place {
755+
if proj.elem == ProjectionElem::Deref {
756+
break;
757+
}
758+
place = &proj.base;
759+
}
741760
if let Place::Local(local) = *place {
742761
if self.mir.local_kind(local) == LocalKind::Temp {
743-
self.promotion_candidates.push(candidate);
762+
if let Some(qualif) = self.temp_qualif[local] {
763+
// `forbidden_mut` is false, so we can safely ignore
764+
// `MUTABLE_INTERIOR` from the local's qualifications.
765+
// This allows borrowing fields which don't have
766+
// `MUTABLE_INTERIOR`, from a type that does, e.g.:
767+
// `let _: &'static _ = &(Cell::new(1), 2).1;`
768+
if self.can_promote(qualif - Qualif::MUTABLE_INTERIOR) {
769+
self.promotion_candidates.push(candidate);
770+
}
771+
}
744772
}
745773
}
746774
}
@@ -897,7 +925,7 @@ This does not pose a problem by itself because they can't be accessed directly."
897925
}
898926
let candidate = Candidate::Argument { bb, index: i };
899927
if is_shuffle && i == 2 {
900-
if this.can_promote() {
928+
if this.can_promote(this.qualif) {
901929
this.promotion_candidates.push(candidate);
902930
} else {
903931
span_err!(this.tcx.sess, this.span, E0526,
@@ -913,7 +941,7 @@ This does not pose a problem by itself because they can't be accessed directly."
913941
if !constant_arguments.contains(&i) {
914942
return
915943
}
916-
if this.can_promote() {
944+
if this.can_promote(this.qualif) {
917945
this.promotion_candidates.push(candidate);
918946
} else {
919947
this.tcx.sess.span_err(this.span,

src/test/mir-opt/end_region_destruction_extents_1.rs

+6-2
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);

src/test/mir-opt/match_false_edges.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ fn main() {
8888
// }
8989
// bb9: { // binding1 and guard
9090
// StorageLive(_5);
91-
// _5 = &((_2 as Some).0: i32);
91+
// _11 = promoted[0];
92+
// _5 = &(((*_11) as Some).0: i32);
9293
// StorageLive(_8);
9394
// _8 = const guard() -> [return: bb10, unwind: bb1];
9495
// }

0 commit comments

Comments
 (0)