Skip to content

Commit 0198a1e

Browse files
committed
Auto merge of rust-lang#53909 - mikhail-m1:53643, r=nikomatsakis
Skip a shared borrow of a immutable local variables issue rust-lang#53643 r? @nikomatsakis
2 parents 968d95e + d0c1e5a commit 0198a1e

File tree

18 files changed

+822
-478
lines changed

18 files changed

+822
-478
lines changed

src/librustc/mir/mod.rs

+19-19
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,6 @@ impl<'tcx> Mir<'tcx> {
249249
} else if self.local_decls[local].name.is_some() {
250250
LocalKind::Var
251251
} else {
252-
debug_assert!(
253-
self.local_decls[local].mutability == Mutability::Mut,
254-
"temp should be mutable"
255-
);
256-
257252
LocalKind::Temp
258253
}
259254
}
@@ -782,33 +777,38 @@ impl<'tcx> LocalDecl<'tcx> {
782777
/// Create a new `LocalDecl` for a temporary.
783778
#[inline]
784779
pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self {
785-
LocalDecl {
786-
mutability: Mutability::Mut,
787-
ty,
788-
name: None,
789-
source_info: SourceInfo {
790-
span,
791-
scope: OUTERMOST_SOURCE_SCOPE,
792-
},
793-
visibility_scope: OUTERMOST_SOURCE_SCOPE,
794-
internal: false,
795-
is_user_variable: None,
796-
}
780+
Self::new_local(ty, Mutability::Mut, false, span)
781+
}
782+
783+
/// Create a new immutable `LocalDecl` for a temporary.
784+
#[inline]
785+
pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
786+
Self::new_local(ty, Mutability::Not, false, span)
797787
}
798788

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

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_constant.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1818
/// Compile `expr`, yielding a compile-time constant. Assumes that
1919
/// `expr` is a valid compile-time constant!
2020
pub fn as_constant<M>(&mut self, expr: M) -> Constant<'tcx>
21-
where M: Mirror<'tcx, Output=Expr<'tcx>>
21+
where
22+
M: Mirror<'tcx, Output = Expr<'tcx>>,
2223
{
2324
let expr = self.hir.mirror(expr);
2425
self.expr_as_constant(expr)
2526
}
2627

2728
fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> {
2829
let this = self;
29-
let Expr { ty, temp_lifetime: _, span, kind }
30-
= expr;
30+
let Expr {
31+
ty,
32+
temp_lifetime: _,
33+
span,
34+
kind,
35+
} = expr;
3136
match kind {
32-
ExprKind::Scope { region_scope: _, lint_level: _, value } =>
33-
this.as_constant(value),
34-
ExprKind::Literal { literal, user_ty } =>
35-
Constant { span, ty, user_ty, literal },
36-
_ =>
37-
span_bug!(
38-
span,
39-
"expression is not a valid constant {:?}",
40-
kind),
37+
ExprKind::Scope {
38+
region_scope: _,
39+
lint_level: _,
40+
value,
41+
} => this.as_constant(value),
42+
ExprKind::Literal { literal, user_ty } => Constant {
43+
span,
44+
ty,
45+
user_ty,
46+
literal,
47+
},
48+
_ => span_bug!(span, "expression is not a valid constant {:?}", kind),
4149
}
4250
}
4351
}

0 commit comments

Comments
 (0)