Skip to content

Commit 0a3761e

Browse files
committed
Auto merge of #46984 - arielb1:pre-statement-effect, r=nikomatsakis
NLL fixes First, introduce pre-statement effects to dataflow to fix #46875. Edge dataflow effects might make that redundant, but I'm not sure of the best way to integrate them with liveness etc., and if this is a hack, this is one of the cleanest hacks I've seen. And I want a small fix to avoid the torrent of bug reports. Second, fix linking of projections to fix #46974 r? @pnkfelix
2 parents d96cc6e + bd1bd76 commit 0a3761e

12 files changed

+262
-29
lines changed

src/librustc_mir/borrow_check/flows.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ impl<'b, 'gcx, 'tcx> fmt::Display for Flows<'b, 'gcx, 'tcx> {
8888
};
8989
saw_one = true;
9090
let borrow_data = &self.borrows.operator().borrows()[borrow.borrow_index()];
91-
s.push_str(&format!("{}", borrow_data));
91+
s.push_str(&format!("{}{}", borrow_data,
92+
if borrow.is_activation() { "@active" } else { "" }));
9293
});
9394
s.push_str("] ");
9495

src/librustc_mir/borrow_check/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
20122012
let borrowed = &data[i.borrow_index()];
20132013

20142014
if self.places_conflict(&borrowed.borrowed_place, place, access) {
2015+
debug!("each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
2016+
i, borrowed, place, access);
20152017
let ctrl = op(self, i, borrowed);
20162018
if ctrl == Control::Break {
20172019
return;

src/librustc_mir/borrow_check/nll/constraint_generation.rs

+75-22
Original file line numberDiff line numberDiff line change
@@ -130,38 +130,91 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
130130
});
131131
}
132132

133+
// Add the reborrow constraint at `location` so that `borrowed_place`
134+
// is valid for `borrow_region`.
133135
fn add_reborrow_constraint(
134136
&mut self,
135137
location: Location,
136138
borrow_region: ty::Region<'tcx>,
137139
borrowed_place: &Place<'tcx>,
138140
) {
139-
if let Projection(ref proj) = *borrowed_place {
140-
let PlaceProjection { ref base, ref elem } = **proj;
141-
142-
if let ProjectionElem::Deref = *elem {
143-
let tcx = self.infcx.tcx;
144-
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
145-
let base_sty = &base_ty.sty;
146-
147-
if let ty::TyRef(base_region, ty::TypeAndMut { ty: _, mutbl }) = *base_sty {
148-
match mutbl {
149-
hir::Mutability::MutImmutable => {}
150-
151-
hir::Mutability::MutMutable => {
152-
self.add_reborrow_constraint(location, borrow_region, base);
141+
let mut borrowed_place = borrowed_place;
142+
143+
debug!("add_reborrow_constraint({:?}, {:?}, {:?})",
144+
location, borrow_region, borrowed_place);
145+
while let Projection(box PlaceProjection { base, elem }) = borrowed_place {
146+
debug!("add_reborrow_constraint - iteration {:?}", borrowed_place);
147+
148+
match *elem {
149+
ProjectionElem::Deref => {
150+
let tcx = self.infcx.tcx;
151+
let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
152+
153+
debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
154+
match base_ty.sty {
155+
ty::TyRef(ref_region, ty::TypeAndMut { ty: _, mutbl }) => {
156+
let span = self.mir.source_info(location).span;
157+
self.regioncx.add_outlives(
158+
span,
159+
ref_region.to_region_vid(),
160+
borrow_region.to_region_vid(),
161+
location.successor_within_block(),
162+
);
163+
164+
match mutbl {
165+
hir::Mutability::MutImmutable => {
166+
// Immutable reference. We don't need the base
167+
// to be valid for the entire lifetime of
168+
// the borrow.
169+
break
170+
}
171+
hir::Mutability::MutMutable => {
172+
// Mutable reference. We *do* need the base
173+
// to be valid, because after the base becomes
174+
// invalid, someone else can use our mutable deref.
175+
176+
// This is in order to make the following function
177+
// illegal:
178+
// ```
179+
// fn unsafe_deref<'a, 'b>(x: &'a &'b mut T) -> &'b mut T {
180+
// &mut *x
181+
// }
182+
// ```
183+
//
184+
// As otherwise you could clone `&mut T` using the
185+
// following function:
186+
// ```
187+
// fn bad(x: &mut T) -> (&mut T, &mut T) {
188+
// let my_clone = unsafe_deref(&'a x);
189+
// ENDREGION 'a;
190+
// (my_clone, x)
191+
// }
192+
// ```
193+
}
194+
}
195+
}
196+
ty::TyRawPtr(..) => {
197+
// deref of raw pointer, guaranteed to be valid
198+
break
153199
}
200+
ty::TyAdt(def, _) if def.is_box() => {
201+
// deref of `Box`, need the base to be valid - propagate
202+
}
203+
_ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place)
154204
}
155-
156-
let span = self.mir.source_info(location).span;
157-
self.regioncx.add_outlives(
158-
span,
159-
base_region.to_region_vid(),
160-
borrow_region.to_region_vid(),
161-
location.successor_within_block(),
162-
);
205+
}
206+
ProjectionElem::Field(..) |
207+
ProjectionElem::Downcast(..) |
208+
ProjectionElem::Index(..) |
209+
ProjectionElem::ConstantIndex { .. } |
210+
ProjectionElem::Subslice { .. } => {
211+
// other field access
163212
}
164213
}
214+
215+
// The "propagate" case. We need to check that our base is valid
216+
// for the borrow's lifetime.
217+
borrowed_place = base;
165218
}
166219
}
167220
}

src/librustc_mir/dataflow/at_location.rs

+24
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ impl<BD> FlowsAtLocation for FlowAtLocation<BD>
149149
fn reconstruct_statement_effect(&mut self, loc: Location) {
150150
self.stmt_gen.reset_to_empty();
151151
self.stmt_kill.reset_to_empty();
152+
{
153+
let mut sets = BlockSets {
154+
on_entry: &mut self.curr_state,
155+
gen_set: &mut self.stmt_gen,
156+
kill_set: &mut self.stmt_kill,
157+
};
158+
self.base_results
159+
.operator()
160+
.before_statement_effect(&mut sets, loc);
161+
}
162+
self.apply_local_effect(loc);
163+
152164
let mut sets = BlockSets {
153165
on_entry: &mut self.curr_state,
154166
gen_set: &mut self.stmt_gen,
@@ -162,6 +174,18 @@ impl<BD> FlowsAtLocation for FlowAtLocation<BD>
162174
fn reconstruct_terminator_effect(&mut self, loc: Location) {
163175
self.stmt_gen.reset_to_empty();
164176
self.stmt_kill.reset_to_empty();
177+
{
178+
let mut sets = BlockSets {
179+
on_entry: &mut self.curr_state,
180+
gen_set: &mut self.stmt_gen,
181+
kill_set: &mut self.stmt_kill,
182+
};
183+
self.base_results
184+
.operator()
185+
.before_terminator_effect(&mut sets, loc);
186+
}
187+
self.apply_local_effect(loc);
188+
165189
let mut sets = BlockSets {
166190
on_entry: &mut self.curr_state,
167191
gen_set: &mut self.stmt_gen,

src/librustc_mir/dataflow/impls/borrows.rs

+28
Original file line numberDiff line numberDiff line change
@@ -649,13 +649,27 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Reservations<'a, 'gcx, 'tcx> {
649649
// `_sets`.
650650
}
651651

652+
fn before_statement_effect(&self,
653+
sets: &mut BlockSets<ReserveOrActivateIndex>,
654+
location: Location) {
655+
debug!("Reservations::before_statement_effect sets: {:?} location: {:?}", sets, location);
656+
self.0.kill_loans_out_of_scope_at_location(sets, location, false);
657+
}
658+
652659
fn statement_effect(&self,
653660
sets: &mut BlockSets<ReserveOrActivateIndex>,
654661
location: Location) {
655662
debug!("Reservations::statement_effect sets: {:?} location: {:?}", sets, location);
656663
self.0.statement_effect_on_borrows(sets, location, false);
657664
}
658665

666+
fn before_terminator_effect(&self,
667+
sets: &mut BlockSets<ReserveOrActivateIndex>,
668+
location: Location) {
669+
debug!("Reservations::before_terminator_effect sets: {:?} location: {:?}", sets, location);
670+
self.0.kill_loans_out_of_scope_at_location(sets, location, false);
671+
}
672+
659673
fn terminator_effect(&self,
660674
sets: &mut BlockSets<ReserveOrActivateIndex>,
661675
location: Location) {
@@ -696,13 +710,27 @@ impl<'a, 'gcx, 'tcx> BitDenotation for ActiveBorrows<'a, 'gcx, 'tcx> {
696710
// `_sets`.
697711
}
698712

713+
fn before_statement_effect(&self,
714+
sets: &mut BlockSets<ReserveOrActivateIndex>,
715+
location: Location) {
716+
debug!("ActiveBorrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
717+
self.0.kill_loans_out_of_scope_at_location(sets, location, true);
718+
}
719+
699720
fn statement_effect(&self,
700721
sets: &mut BlockSets<ReserveOrActivateIndex>,
701722
location: Location) {
702723
debug!("ActiveBorrows::statement_effect sets: {:?} location: {:?}", sets, location);
703724
self.0.statement_effect_on_borrows(sets, location, true);
704725
}
705726

727+
fn before_terminator_effect(&self,
728+
sets: &mut BlockSets<ReserveOrActivateIndex>,
729+
location: Location) {
730+
debug!("ActiveBorrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
731+
self.0.kill_loans_out_of_scope_at_location(sets, location, true);
732+
}
733+
706734
fn terminator_effect(&self,
707735
sets: &mut BlockSets<ReserveOrActivateIndex>,
708736
location: Location) {

src/librustc_mir/dataflow/mod.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
214214
}
215215
for j_stmt in 0..statements.len() {
216216
let location = Location { block: bb, statement_index: j_stmt };
217+
self.flow_state.operator.before_statement_effect(sets, location);
217218
self.flow_state.operator.statement_effect(sets, location);
218219
if track_intrablock {
219220
sets.apply_local_effect();
@@ -222,6 +223,7 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
222223

223224
if terminator.is_some() {
224225
let location = Location { block: bb, statement_index: statements.len() };
226+
self.flow_state.operator.before_terminator_effect(sets, location);
225227
self.flow_state.operator.terminator_effect(sets, location);
226228
if track_intrablock {
227229
sets.apply_local_effect();
@@ -365,9 +367,10 @@ pub(crate) trait DataflowResultsConsumer<'a, 'tcx: 'a> {
365367
fn mir(&self) -> &'a Mir<'tcx>;
366368
}
367369

368-
pub fn state_for_location<T: BitDenotation>(loc: Location,
369-
analysis: &T,
370-
result: &DataflowResults<T>)
370+
pub fn state_for_location<'tcx, T: BitDenotation>(loc: Location,
371+
analysis: &T,
372+
result: &DataflowResults<T>,
373+
mir: &Mir<'tcx>)
371374
-> IdxSetBuf<T::Idx> {
372375
let mut entry = result.sets().on_entry_set_for(loc.block.index()).to_owned();
373376

@@ -381,8 +384,16 @@ pub fn state_for_location<T: BitDenotation>(loc: Location,
381384
for stmt in 0..loc.statement_index {
382385
let mut stmt_loc = loc;
383386
stmt_loc.statement_index = stmt;
387+
analysis.before_statement_effect(&mut sets, stmt_loc);
384388
analysis.statement_effect(&mut sets, stmt_loc);
385389
}
390+
391+
// Apply the pre-statement effect of the statement we're evaluating.
392+
if loc.statement_index == mir[loc.block].statements.len() {
393+
analysis.before_terminator_effect(&mut sets, loc);
394+
} else {
395+
analysis.before_statement_effect(&mut sets, loc);
396+
}
386397
}
387398

388399
entry
@@ -637,6 +648,21 @@ pub trait BitDenotation: BitwiseOperator {
637648
/// (For example, establishing the call arguments.)
638649
fn start_block_effect(&self, entry_set: &mut IdxSet<Self::Idx>);
639650

651+
/// Similar to `statement_effect`, except it applies
652+
/// *just before* the statement rather than *just after* it.
653+
///
654+
/// This matters for "dataflow at location" APIs, because the
655+
/// before-statement effect is visible while visiting the
656+
/// statement, while the after-statement effect only becomes
657+
/// visible at the next statement.
658+
///
659+
/// Both the before-statement and after-statement effects are
660+
/// applied, in that order, before moving for the next
661+
/// statement.
662+
fn before_statement_effect(&self,
663+
_sets: &mut BlockSets<Self::Idx>,
664+
_location: Location) {}
665+
640666
/// Mutates the block-sets (the flow sets for the given
641667
/// basic block) according to the effects of evaluating statement.
642668
///
@@ -651,6 +677,21 @@ pub trait BitDenotation: BitwiseOperator {
651677
sets: &mut BlockSets<Self::Idx>,
652678
location: Location);
653679

680+
/// Similar to `terminator_effect`, except it applies
681+
/// *just before* the terminator rather than *just after* it.
682+
///
683+
/// This matters for "dataflow at location" APIs, because the
684+
/// before-terminator effect is visible while visiting the
685+
/// terminator, while the after-terminator effect only becomes
686+
/// visible at the terminator's successors.
687+
///
688+
/// Both the before-terminator and after-terminator effects are
689+
/// applied, in that order, before moving for the next
690+
/// terminator.
691+
fn before_terminator_effect(&self,
692+
_sets: &mut BlockSets<Self::Idx>,
693+
_location: Location) {}
694+
654695
/// Mutates the block-sets (the flow sets for the given
655696
/// basic block) according to the effects of evaluating
656697
/// the terminator.

src/librustc_mir/transform/generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
363363
statement_index: data.statements.len(),
364364
};
365365

366-
let storage_liveness = state_for_location(loc, &analysis, &storage_live);
366+
let storage_liveness = state_for_location(loc, &analysis, &storage_live, mir);
367367

368368
storage_liveness_map.insert(block, storage_liveness.clone());
369369

src/librustc_mir/transform/rustc_peek.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,18 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
203203
// reset GEN and KILL sets before emulating their effect.
204204
for e in sets.gen_set.words_mut() { *e = 0; }
205205
for e in sets.kill_set.words_mut() { *e = 0; }
206-
results.0.operator.statement_effect(&mut sets, Location { block: bb, statement_index: j });
206+
results.0.operator.before_statement_effect(
207+
&mut sets, Location { block: bb, statement_index: j });
208+
results.0.operator.statement_effect(
209+
&mut sets, Location { block: bb, statement_index: j });
207210
sets.on_entry.union(sets.gen_set);
208211
sets.on_entry.subtract(sets.kill_set);
209212
}
210213

214+
results.0.operator.before_terminator_effect(
215+
&mut sets,
216+
Location { block: bb, statement_index: statements.len() });
217+
211218
tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
212219
anticipated pattern; note that \
213220
rustc_peek expects input of \

src/test/compile-fail/borrowck/two-phase-activation-sharing-interference.rs

-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ fn should_also_eventually_be_ok_with_nll() {
5656
let _z = &x;
5757
*y += 1;
5858
//[lxl]~^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
59-
//[nll]~^^ ERROR cannot borrow `x` as mutable because it is also borrowed as immutable
6059
}
6160

6261
fn main() { }
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2017 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(nll)]
12+
13+
// run-pass
14+
15+
fn vec() {
16+
let mut _x = vec!['c'];
17+
let _y = &_x;
18+
_x = Vec::new();
19+
}
20+
21+
fn int() {
22+
let mut _x = 5;
23+
let _y = &_x;
24+
_x = 7;
25+
}
26+
27+
fn main() {
28+
vec();
29+
int();
30+
}

0 commit comments

Comments
 (0)