Skip to content

Commit 527a29a

Browse files
committed
Skip a shared borrow of a immutable local variables
issue rust-lang#53643
1 parent 35a5541 commit 527a29a

File tree

13 files changed

+192
-58
lines changed

13 files changed

+192
-58
lines changed

src/librustc/mir/mod.rs

+19-19
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,6 @@ impl<'tcx> Mir<'tcx> {
252252
} else if self.local_decls[local].name.is_some() {
253253
LocalKind::Var
254254
} else {
255-
debug_assert!(
256-
self.local_decls[local].mutability == Mutability::Mut,
257-
"temp should be mutable"
258-
);
259-
260255
LocalKind::Temp
261256
}
262257
}
@@ -784,33 +779,38 @@ impl<'tcx> LocalDecl<'tcx> {
784779
/// Create a new `LocalDecl` for a temporary.
785780
#[inline]
786781
pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self {
787-
LocalDecl {
788-
mutability: Mutability::Mut,
789-
ty,
790-
name: None,
791-
source_info: SourceInfo {
792-
span,
793-
scope: OUTERMOST_SOURCE_SCOPE,
794-
},
795-
visibility_scope: OUTERMOST_SOURCE_SCOPE,
796-
internal: false,
797-
is_user_variable: None,
798-
}
782+
Self::new_local(ty, Mutability::Mut, false, span)
783+
}
784+
785+
/// Create a new immutable `LocalDecl` for a temporary.
786+
#[inline]
787+
pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
788+
Self::new_local(ty, Mutability::Not, false, span)
799789
}
800790

801791
/// Create a new `LocalDecl` for a internal temporary.
802792
#[inline]
803793
pub fn new_internal(ty: Ty<'tcx>, span: Span) -> Self {
794+
Self::new_local(ty, Mutability::Mut, true, span)
795+
}
796+
797+
#[inline]
798+
fn new_local(
799+
ty: Ty<'tcx>,
800+
mutability: Mutability,
801+
internal: bool,
802+
span: Span,
803+
) -> Self {
804804
LocalDecl {
805-
mutability: Mutability::Mut,
805+
mutability,
806806
ty,
807807
name: None,
808808
source_info: SourceInfo {
809809
span,
810810
scope: OUTERMOST_SOURCE_SCOPE,
811811
},
812812
visibility_scope: OUTERMOST_SOURCE_SCOPE,
813-
internal: true,
813+
internal,
814814
is_user_variable: None,
815815
}
816816
}

src/librustc_mir/borrow_check/borrow_set.rs

+57-3
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010

1111
use borrow_check::place_ext::PlaceExt;
1212
use dataflow::indexes::BorrowIndex;
13+
use dataflow::move_paths::MoveData;
1314
use rustc::mir::traversal;
1415
use rustc::mir::visit::{PlaceContext, Visitor};
15-
use rustc::mir::{self, Location, Mir, Place};
16+
use rustc::mir::{self, Location, Mir, Place, Local};
1617
use rustc::ty::{Region, TyCtxt};
1718
use rustc::util::nodemap::{FxHashMap, FxHashSet};
1819
use rustc_data_structures::indexed_vec::IndexVec;
20+
use rustc_data_structures::bitvec::BitArray;
1921
use std::fmt;
2022
use std::hash::Hash;
2123
use std::ops::Index;
@@ -43,6 +45,8 @@ crate struct BorrowSet<'tcx> {
4345

4446
/// Map from local to all the borrows on that local
4547
crate local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
48+
49+
crate locals_state_at_exit: LocalsStateAtExit,
4650
}
4751

4852
impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
@@ -96,8 +100,52 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
96100
}
97101
}
98102

103+
crate enum LocalsStateAtExit {
104+
AllAreInvalidated,
105+
SomeAreInvalidated { has_storage_dead_or_moved: BitArray<Local> }
106+
}
107+
108+
impl LocalsStateAtExit {
109+
fn build(
110+
locals_are_invalidated_at_exit: bool,
111+
mir: &Mir<'tcx>,
112+
move_data: &MoveData<'tcx>
113+
) -> Self {
114+
struct HasStorageDead(BitArray<Local>);
115+
116+
impl<'tcx> Visitor<'tcx> for HasStorageDead {
117+
fn visit_local(&mut self, local: &Local, ctx: PlaceContext<'tcx>, _: Location) {
118+
if ctx == PlaceContext::StorageDead {
119+
self.0.insert(*local);
120+
}
121+
}
122+
}
123+
124+
if locals_are_invalidated_at_exit {
125+
LocalsStateAtExit::AllAreInvalidated
126+
} else {
127+
let mut has_storage_dead = HasStorageDead(BitArray::new(mir.local_decls.len()));
128+
has_storage_dead.visit_mir(mir);
129+
let mut has_storage_dead_or_moved = has_storage_dead.0;
130+
for move_out in &move_data.moves {
131+
if let Some(index) = move_data.base_local(move_out.path) {
132+
has_storage_dead_or_moved.insert(index);
133+
134+
}
135+
}
136+
LocalsStateAtExit::SomeAreInvalidated{ has_storage_dead_or_moved }
137+
}
138+
}
139+
}
140+
99141
impl<'tcx> BorrowSet<'tcx> {
100-
pub fn build(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self {
142+
pub fn build(
143+
tcx: TyCtxt<'_, '_, 'tcx>,
144+
mir: &Mir<'tcx>,
145+
locals_are_invalidated_at_exit: bool,
146+
move_data: &MoveData<'tcx>
147+
) -> Self {
148+
101149
let mut visitor = GatherBorrows {
102150
tcx,
103151
mir,
@@ -107,6 +155,8 @@ impl<'tcx> BorrowSet<'tcx> {
107155
region_map: FxHashMap(),
108156
local_map: FxHashMap(),
109157
pending_activations: FxHashMap(),
158+
locals_state_at_exit:
159+
LocalsStateAtExit::build(locals_are_invalidated_at_exit, mir, move_data),
110160
};
111161

112162
for (block, block_data) in traversal::preorder(mir) {
@@ -119,6 +169,7 @@ impl<'tcx> BorrowSet<'tcx> {
119169
activation_map: visitor.activation_map,
120170
region_map: visitor.region_map,
121171
local_map: visitor.local_map,
172+
locals_state_at_exit: visitor.locals_state_at_exit,
122173
}
123174
}
124175

@@ -148,6 +199,8 @@ struct GatherBorrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
148199
/// the borrow. When we find a later use of this activation, we
149200
/// remove from the map (and add to the "tombstone" set below).
150201
pending_activations: FxHashMap<mir::Local, BorrowIndex>,
202+
203+
locals_state_at_exit: LocalsStateAtExit,
151204
}
152205

153206
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
@@ -159,7 +212,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
159212
location: mir::Location,
160213
) {
161214
if let mir::Rvalue::Ref(region, kind, ref borrowed_place) = *rvalue {
162-
if borrowed_place.ignore_borrow(self.tcx, self.mir) {
215+
if borrowed_place.ignore_borrow(
216+
self.tcx, self.mir, &self.locals_state_at_exit) {
163217
return;
164218
}
165219

src/librustc_mir/borrow_check/mod.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
196196
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
197197
));
198198

199-
let borrow_set = Rc::new(BorrowSet::build(tcx, mir));
199+
let locals_are_invalidated_at_exit = match tcx.hir.body_owner_kind(id) {
200+
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false,
201+
hir::BodyOwnerKind::Fn => true,
202+
};
203+
let borrow_set = Rc::new(BorrowSet::build(
204+
tcx, mir, locals_are_invalidated_at_exit, &mdpe.move_data));
200205

201206
// If we are in non-lexical mode, compute the non-lexical lifetimes.
202207
let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions(
@@ -241,10 +246,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
241246
param_env: param_env,
242247
location_table,
243248
movable_generator,
244-
locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
245-
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => false,
246-
hir::BodyOwnerKind::Fn => true,
247-
},
249+
locals_are_invalidated_at_exit,
248250
access_place_error_reported: FxHashSet(),
249251
reservation_error_reported: FxHashSet(),
250252
moved_error_reported: FxHashSet(),

src/librustc_mir/borrow_check/place_ext.rs

+38-7
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,57 @@
1010

1111
use rustc::hir;
1212
use rustc::mir::ProjectionElem;
13-
use rustc::mir::{Local, Mir, Place};
13+
use rustc::mir::{Local, Mir, Place, Mutability};
1414
use rustc::ty::{self, TyCtxt};
15+
use borrow_check::borrow_set::LocalsStateAtExit;
1516

1617
/// Extension methods for the `Place` type.
1718
crate trait PlaceExt<'tcx> {
1819
/// Returns true if we can safely ignore borrows of this place.
1920
/// This is true whenever there is no action that the user can do
2021
/// to the place `self` that would invalidate the borrow. This is true
2122
/// for borrows of raw pointer dereferents as well as shared references.
22-
fn ignore_borrow(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool;
23+
fn ignore_borrow(
24+
&self,
25+
tcx: TyCtxt<'_, '_, 'tcx>,
26+
mir: &Mir<'tcx>,
27+
locals_state_at_exit: &LocalsStateAtExit,
28+
) -> bool;
2329

2430
/// If this is a place like `x.f.g`, returns the local
2531
/// `x`. Returns `None` if this is based in a static.
2632
fn root_local(&self) -> Option<Local>;
2733
}
2834

2935
impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
30-
fn ignore_borrow(&self, tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> bool {
36+
fn ignore_borrow(
37+
&self,
38+
tcx: TyCtxt<'_, '_, 'tcx>,
39+
mir: &Mir<'tcx>,
40+
locals_state_at_exit: &LocalsStateAtExit,
41+
) -> bool {
3142
match self {
32-
Place::Promoted(_) |
33-
Place::Local(_) => false,
43+
Place::Promoted(_) => false,
44+
45+
// If a local variable is immutable, then we only need to track borrows to guard
46+
// against two kinds of errors:
47+
// * The variable being dropped while still borrowed (e.g., because the fn returns
48+
// a reference to a local variable)
49+
// * The variable being moved while still borrowed
50+
//
51+
// In particular, the variable cannot be mutated -- the "access checks" will fail --
52+
// so we don't have to worry about mutation while borrowed.
53+
Place::Local(index) => {
54+
match locals_state_at_exit {
55+
LocalsStateAtExit::AllAreInvalidated => false,
56+
LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
57+
let ignore = !has_storage_dead_or_moved.contains(*index) &&
58+
mir.local_decls[*index].mutability == Mutability::Not;
59+
debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
60+
ignore
61+
}
62+
}
63+
}
3464
Place::Static(static_) => {
3565
tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable)
3666
}
@@ -39,7 +69,8 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
3969
| ProjectionElem::Downcast(..)
4070
| ProjectionElem::Subslice { .. }
4171
| ProjectionElem::ConstantIndex { .. }
42-
| ProjectionElem::Index(_) => proj.base.ignore_borrow(tcx, mir),
72+
| ProjectionElem::Index(_) => proj.base.ignore_borrow(
73+
tcx, mir, locals_state_at_exit),
4374

4475
ProjectionElem::Deref => {
4576
let ty = proj.base.ty(mir, tcx).to_ty(tcx);
@@ -55,7 +86,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
5586
// borrowed *that* one, leaving the original
5687
// path unborrowed.
5788
ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true,
58-
_ => proj.base.ignore_borrow(tcx, mir),
89+
_ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit),
5990
}
6091
}
6192
},

src/librustc_mir/build/expr/as_operand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
7373
Category::Place |
7474
Category::Rvalue(..) => {
7575
let operand =
76-
unpack!(block = this.as_temp(block, scope, expr));
76+
unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
7777
block.and(Operand::Move(Place::Local(operand)))
7878
}
7979
}

src/librustc_mir/build/expr/as_place.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,42 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
2828
where M: Mirror<'tcx, Output=Expr<'tcx>>
2929
{
3030
let expr = self.hir.mirror(expr);
31-
self.expr_as_place(block, expr)
31+
self.expr_as_place(block, expr, Mutability::Mut)
32+
}
33+
34+
/// Compile `expr`, yielding a place that we can move from etc.
35+
/// Mutability note: The caller of this method promises only to read from the resulting
36+
/// place. The place itself may or may not be mutable:
37+
/// * If this expr is a place expr like a.b, then we will return that place.
38+
/// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
39+
pub fn as_read_only_place<M>(&mut self,
40+
block: BasicBlock,
41+
expr: M)
42+
-> BlockAnd<Place<'tcx>>
43+
where M: Mirror<'tcx, Output=Expr<'tcx>>
44+
{
45+
let expr = self.hir.mirror(expr);
46+
self.expr_as_place(block, expr, Mutability::Not)
3247
}
3348

3449
fn expr_as_place(&mut self,
3550
mut block: BasicBlock,
36-
expr: Expr<'tcx>)
51+
expr: Expr<'tcx>,
52+
mutability: Mutability)
3753
-> BlockAnd<Place<'tcx>> {
38-
debug!("expr_as_place(block={:?}, expr={:?})", block, expr);
54+
debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
3955

4056
let this = self;
4157
let expr_span = expr.span;
4258
let source_info = this.source_info(expr_span);
4359
match expr.kind {
4460
ExprKind::Scope { region_scope, lint_level, value } => {
4561
this.in_scope((region_scope, source_info), lint_level, block, |this| {
46-
this.as_place(block, value)
62+
if mutability == Mutability::Not {
63+
this.as_read_only_place(block, value)
64+
} else {
65+
this.as_place(block, value)
66+
}
4767
})
4868
}
4969
ExprKind::Field { lhs, name } => {
@@ -63,7 +83,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6383
// region_scope=None so place indexes live forever. They are scalars so they
6484
// do not need storage annotations, and they are often copied between
6585
// places.
66-
let idx = unpack!(block = this.as_temp(block, None, index));
86+
let idx = unpack!(block = this.as_temp(block, None, index, Mutability::Mut));
6787

6888
// bounds check:
6989
let (len, lt) = (this.temp(usize_ty.clone(), expr_span),
@@ -137,7 +157,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
137157
Some(Category::Place) => false,
138158
_ => true,
139159
});
140-
let temp = unpack!(block = this.as_temp(block, expr.temp_lifetime, expr));
160+
let temp = unpack!(
161+
block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
141162
block.and(Place::Local(temp))
142163
}
143164
}

src/librustc_mir/build/expr/as_rvalue.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6363
block.and(Rvalue::Repeat(value_operand, count))
6464
}
6565
ExprKind::Borrow { region, borrow_kind, arg } => {
66-
let arg_place = unpack!(block = this.as_place(block, arg));
66+
let arg_place = match borrow_kind {
67+
BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
68+
_ => unpack!(block = this.as_place(block, arg))
69+
};
6770
block.and(Rvalue::Ref(region, borrow_kind, arg_place))
6871
}
6972
ExprKind::Binary { op, lhs, rhs } => {

0 commit comments

Comments
 (0)