diff options
author | bors <bors@rust-lang.org> | 2018-04-17 14:23:57 +0000 |
---|---|---|
committer | bors <bors@rust-lang.org> | 2018-04-17 14:23:57 +0000 |
commit | 881a7cd86ef1001bdfa9590878ca24e57794302f (patch) | |
tree | 947639046e0b0f2a1ddc5d0ad6d6f4e33040ff44 /src/librustc_mir/borrow_check/mod.rs | |
parent | Auto merge of #49626 - fanzier:chalk-lowering, r=scalexm (diff) | |
parent | s/`use_mir`/`use_mir_borrowck`/ (diff) | |
download | grust-881a7cd86ef1001bdfa9590878ca24e57794302f.tar.gz grust-881a7cd86ef1001bdfa9590878ca24e57794302f.tar.bz2 grust-881a7cd86ef1001bdfa9590878ca24e57794302f.tar.xz |
Auto merge of #49836 - nikomatsakis:nll-facts-prep, r=pnkfelix
prep work for using timely dataflow with NLL
Two major changes:
**Two-phase borrows are overhauled.** We no longer have two bits per borrow. Instead, we track -- for each borrow -- an (optional) "activation point". Then, for each point P where the borrow is in scope, we check where P falls relative to the activation point. If P is between the reservation point and the activation point, then this is the "reservation" phase of the borrow, else the borrow is considered active. This is simpler and means that the dataflow doesn't have to care about 2-phase at all, at last not yet.
**We no longer support using the MIR borrow checker without NLL.** It is going to be increasingly untenable to support lexical mode as we go forward, I think, and also of increasingly little value. This also exposed a few bugs in NLL mode due to increased testing.
r? @pnkfelix
cc @bobtwinkles
Diffstat (limited to 'src/librustc_mir/borrow_check/mod.rs')
-rw-r--r-- | src/librustc_mir/borrow_check/mod.rs | 271 |
1 files changed, 157 insertions, 114 deletions
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 87379651c2..4dd8d245d3 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs | |||
@@ -22,13 +22,13 @@ use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}; | |||
22 | use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; | 22 | use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind}; |
23 | use rustc::mir::ClosureRegionRequirements; | 23 | use rustc::mir::ClosureRegionRequirements; |
24 | 24 | ||
25 | use rustc_data_structures::control_flow_graph::dominators::Dominators; | ||
25 | use rustc_data_structures::fx::FxHashSet; | 26 | use rustc_data_structures::fx::FxHashSet; |
26 | use rustc_data_structures::indexed_set::IdxSetBuf; | 27 | use rustc_data_structures::indexed_set::IdxSetBuf; |
27 | use rustc_data_structures::indexed_vec::Idx; | 28 | use rustc_data_structures::indexed_vec::Idx; |
28 | 29 | ||
29 | use std::rc::Rc; | 30 | use std::rc::Rc; |
30 | 31 | ||
31 | use syntax::ast; | ||
32 | use syntax_pos::Span; | 32 | use syntax_pos::Span; |
33 | 33 | ||
34 | use dataflow::{do_dataflow, DebugFormatted}; | 34 | use dataflow::{do_dataflow, DebugFormatted}; |
@@ -37,7 +37,7 @@ use dataflow::MoveDataParamEnv; | |||
37 | use dataflow::{DataflowResultsConsumer}; | 37 | use dataflow::{DataflowResultsConsumer}; |
38 | use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; | 38 | use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; |
39 | use dataflow::{EverInitializedPlaces, MovingOutStatements}; | 39 | use dataflow::{EverInitializedPlaces, MovingOutStatements}; |
40 | use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex}; | 40 | use dataflow::Borrows; |
41 | use dataflow::indexes::BorrowIndex; | 41 | use dataflow::indexes::BorrowIndex; |
42 | use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; | 42 | use dataflow::move_paths::{IllegalMoveOriginKind, MoveError}; |
43 | use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; | 43 | use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; |
@@ -46,12 +46,15 @@ use util::collect_writes::FindAssignments; | |||
46 | 46 | ||
47 | use std::iter; | 47 | use std::iter; |
48 | 48 | ||
49 | use self::borrow_set::{BorrowSet, BorrowData}; | ||
49 | use self::flows::Flows; | 50 | use self::flows::Flows; |
50 | use self::prefixes::PrefixSet; | 51 | use self::prefixes::PrefixSet; |
51 | use self::MutateMode::{JustWrite, WriteAndRead}; | 52 | use self::MutateMode::{JustWrite, WriteAndRead}; |
52 | 53 | ||
54 | crate mod borrow_set; | ||
53 | mod error_reporting; | 55 | mod error_reporting; |
54 | mod flows; | 56 | mod flows; |
57 | crate mod place_ext; | ||
55 | mod prefixes; | 58 | mod prefixes; |
56 | 59 | ||
57 | pub(crate) mod nll; | 60 | pub(crate) mod nll; |
@@ -70,7 +73,7 @@ fn mir_borrowck<'a, 'tcx>( | |||
70 | let input_mir = tcx.mir_validated(def_id); | 73 | let input_mir = tcx.mir_validated(def_id); |
71 | debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); | 74 | debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id)); |
72 | 75 | ||
73 | if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.use_mir() { | 76 | if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.use_mir_borrowck() { |
74 | return None; | 77 | return None; |
75 | } | 78 | } |
76 | 79 | ||
@@ -95,19 +98,13 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( | |||
95 | .as_local_node_id(def_id) | 98 | .as_local_node_id(def_id) |
96 | .expect("do_mir_borrowck: non-local DefId"); | 99 | .expect("do_mir_borrowck: non-local DefId"); |
97 | 100 | ||
98 | // Make our own copy of the MIR. This copy will be modified (in place) to | 101 | // Replace all regions with fresh inference variables. This |
99 | // contain non-lexical lifetimes. It will have a lifetime tied | 102 | // requires first making our own copy of the MIR. This copy will |
100 | // to the inference context. | 103 | // be modified (in place) to contain non-lexical lifetimes. It |
104 | // will have a lifetime tied to the inference context. | ||
101 | let mut mir: Mir<'tcx> = input_mir.clone(); | 105 | let mut mir: Mir<'tcx> = input_mir.clone(); |
102 | let free_regions = if !tcx.nll() { | 106 | let free_regions = nll::replace_regions_in_mir(infcx, def_id, param_env, &mut mir); |
103 | None | 107 | let mir = &mir; // no further changes |
104 | } else { | ||
105 | let mir = &mut mir; | ||
106 | |||
107 | // Replace all regions with fresh inference variables. | ||
108 | Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir)) | ||
109 | }; | ||
110 | let mir = &mir; | ||
111 | 108 | ||
112 | let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { | 109 | let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { |
113 | Ok(move_data) => move_data, | 110 | Ok(move_data) => move_data, |
@@ -189,22 +186,20 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( | |||
189 | |bd, i| DebugFormatted::new(&bd.move_data().inits[i]), | 186 | |bd, i| DebugFormatted::new(&bd.move_data().inits[i]), |
190 | )); | 187 | )); |
191 | 188 | ||
189 | let borrow_set = Rc::new(BorrowSet::build(tcx, mir)); | ||
190 | |||
192 | // If we are in non-lexical mode, compute the non-lexical lifetimes. | 191 | // If we are in non-lexical mode, compute the non-lexical lifetimes. |
193 | let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions { | 192 | let (regioncx, opt_closure_req) = nll::compute_regions( |
194 | let (regioncx, opt_closure_req) = nll::compute_regions( | 193 | infcx, |
195 | infcx, | 194 | def_id, |
196 | def_id, | 195 | free_regions, |
197 | free_regions, | 196 | mir, |
198 | mir, | 197 | param_env, |
199 | param_env, | 198 | &mut flow_inits, |
200 | &mut flow_inits, | 199 | &mdpe.move_data, |
201 | &mdpe.move_data, | 200 | &borrow_set, |
202 | ); | 201 | ); |
203 | (Some(Rc::new(regioncx)), opt_closure_req) | 202 | let regioncx = Rc::new(regioncx); |
204 | } else { | ||
205 | assert!(!tcx.nll()); | ||
206 | (None, None) | ||
207 | }; | ||
208 | let flow_inits = flow_inits; // remove mut | 203 | let flow_inits = flow_inits; // remove mut |
209 | 204 | ||
210 | let flow_borrows = FlowAtLocation::new(do_dataflow( | 205 | let flow_borrows = FlowAtLocation::new(do_dataflow( |
@@ -213,24 +208,24 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( | |||
213 | id, | 208 | id, |
214 | &attributes, | 209 | &attributes, |
215 | &dead_unwinds, | 210 | &dead_unwinds, |
216 | Borrows::new(tcx, mir, opt_regioncx.clone(), def_id, body_id), | 211 | Borrows::new(tcx, mir, regioncx.clone(), def_id, body_id, &borrow_set), |
217 | |rs, i| { | 212 | |rs, i| DebugFormatted::new(&rs.location(i)), |
218 | DebugFormatted::new(&(i.kind(), rs.location(i.borrow_index()))) | ||
219 | } | ||
220 | )); | 213 | )); |
221 | 214 | ||
222 | let movable_generator = !match tcx.hir.get(id) { | 215 | let movable_generator = match tcx.hir.get(id) { |
223 | hir::map::Node::NodeExpr(&hir::Expr { | 216 | hir::map::Node::NodeExpr(&hir::Expr { |
224 | node: hir::ExprClosure(.., Some(hir::GeneratorMovability::Static)), | 217 | node: hir::ExprClosure(.., Some(hir::GeneratorMovability::Static)), |
225 | .. | 218 | .. |
226 | }) => true, | 219 | }) => false, |
227 | _ => false, | 220 | _ => true, |
228 | }; | 221 | }; |
229 | 222 | ||
223 | let dominators = mir.dominators(); | ||
224 | |||
230 | let mut mbcx = MirBorrowckCtxt { | 225 | let mut mbcx = MirBorrowckCtxt { |
231 | tcx: tcx, | 226 | tcx: tcx, |
232 | mir: mir, | 227 | mir: mir, |
233 | node_id: id, | 228 | mir_def_id: def_id, |
234 | move_data: &mdpe.move_data, | 229 | move_data: &mdpe.move_data, |
235 | param_env: param_env, | 230 | param_env: param_env, |
236 | movable_generator, | 231 | movable_generator, |
@@ -241,8 +236,10 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( | |||
241 | access_place_error_reported: FxHashSet(), | 236 | access_place_error_reported: FxHashSet(), |
242 | reservation_error_reported: FxHashSet(), | 237 | reservation_error_reported: FxHashSet(), |
243 | moved_error_reported: FxHashSet(), | 238 | moved_error_reported: FxHashSet(), |
244 | nonlexical_regioncx: opt_regioncx, | 239 | nonlexical_regioncx: regioncx, |
245 | nonlexical_cause_info: None, | 240 | nonlexical_cause_info: None, |
241 | borrow_set, | ||
242 | dominators, | ||
246 | }; | 243 | }; |
247 | 244 | ||
248 | let mut state = Flows::new( | 245 | let mut state = Flows::new( |
@@ -262,7 +259,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( | |||
262 | pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { | 259 | pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { |
263 | tcx: TyCtxt<'cx, 'gcx, 'tcx>, | 260 | tcx: TyCtxt<'cx, 'gcx, 'tcx>, |
264 | mir: &'cx Mir<'tcx>, | 261 | mir: &'cx Mir<'tcx>, |
265 | node_id: ast::NodeId, | 262 | mir_def_id: DefId, |
266 | move_data: &'cx MoveData<'tcx>, | 263 | move_data: &'cx MoveData<'tcx>, |
267 | param_env: ParamEnv<'gcx>, | 264 | param_env: ParamEnv<'gcx>, |
268 | movable_generator: bool, | 265 | movable_generator: bool, |
@@ -293,8 +290,14 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> { | |||
293 | /// Non-lexical region inference context, if NLL is enabled. This | 290 | /// Non-lexical region inference context, if NLL is enabled. This |
294 | /// contains the results from region inference and lets us e.g. | 291 | /// contains the results from region inference and lets us e.g. |
295 | /// find out which CFG points are contained in each borrow region. | 292 | /// find out which CFG points are contained in each borrow region. |
296 | nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>, | 293 | nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>, |
297 | nonlexical_cause_info: Option<RegionCausalInfo>, | 294 | nonlexical_cause_info: Option<RegionCausalInfo>, |
295 | |||
296 | /// The set of borrows extracted from the MIR | ||
297 | borrow_set: Rc<BorrowSet<'tcx>>, | ||
298 | |||
299 | /// Dominators for MIR | ||
300 | dominators: Dominators<BasicBlock>, | ||
298 | } | 301 | } |
299 | 302 | ||
300 | // Check that: | 303 | // Check that: |
@@ -535,11 +538,10 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx | |||
535 | 538 | ||
536 | if self.movable_generator { | 539 | if self.movable_generator { |
537 | // Look for any active borrows to locals | 540 | // Look for any active borrows to locals |
538 | let domain = flow_state.borrows.operator(); | 541 | let borrow_set = self.borrow_set.clone(); |
539 | let data = domain.borrows(); | 542 | flow_state.with_outgoing_borrows(|borrows| { |
540 | flow_state.borrows.with_iter_outgoing(|borrows| { | ||
541 | for i in borrows { | 543 | for i in borrows { |
542 | let borrow = &data[i.borrow_index()]; | 544 | let borrow = &borrow_set[i]; |
543 | self.check_for_local_borrow(borrow, span); | 545 | self.check_for_local_borrow(borrow, span); |
544 | } | 546 | } |
545 | }); | 547 | }); |
@@ -551,13 +553,12 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx | |||
551 | // Often, the storage will already have been killed by an explicit | 553 | // Often, the storage will already have been killed by an explicit |
552 | // StorageDead, but we don't always emit those (notably on unwind paths), | 554 | // StorageDead, but we don't always emit those (notably on unwind paths), |
553 | // so this "extra check" serves as a kind of backup. | 555 | // so this "extra check" serves as a kind of backup. |
554 | let domain = flow_state.borrows.operator(); | 556 | let borrow_set = self.borrow_set.clone(); |
555 | let data = domain.borrows(); | 557 | flow_state.with_outgoing_borrows(|borrows| { |
556 | flow_state.borrows.with_iter_outgoing(|borrows| { | ||
557 | for i in borrows { | 558 | for i in borrows { |
558 | let borrow = &data[i.borrow_index()]; | 559 | let borrow = &borrow_set[i]; |
559 | let context = ContextKind::StorageDead.new(loc); | 560 | let context = ContextKind::StorageDead.new(loc); |
560 | self.check_for_invalidation_at_exit(context, borrow, span, flow_state); | 561 | self.check_for_invalidation_at_exit(context, borrow, span); |
561 | } | 562 | } |
562 | }); | 563 | }); |
563 | } | 564 | } |
@@ -836,27 +837,34 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
836 | rw: ReadOrWrite, | 837 | rw: ReadOrWrite, |
837 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | 838 | flow_state: &Flows<'cx, 'gcx, 'tcx>, |
838 | ) -> bool { | 839 | ) -> bool { |
840 | debug!( | ||
841 | "check_access_for_conflict(context={:?}, place_span={:?}, sd={:?}, rw={:?})", | ||
842 | context, | ||
843 | place_span, | ||
844 | sd, | ||
845 | rw, | ||
846 | ); | ||
847 | |||
839 | let mut error_reported = false; | 848 | let mut error_reported = false; |
840 | self.each_borrow_involving_path( | 849 | self.each_borrow_involving_path( |
841 | context, | 850 | context, |
842 | (sd, place_span.0), | 851 | (sd, place_span.0), |
843 | flow_state, | 852 | flow_state, |
844 | |this, index, borrow| match (rw, borrow.kind) { | 853 | |this, borrow_index, borrow| match (rw, borrow.kind) { |
845 | // Obviously an activation is compatible with its own | 854 | // Obviously an activation is compatible with its own |
846 | // reservation (or even prior activating uses of same | 855 | // reservation (or even prior activating uses of same |
847 | // borrow); so don't check if they interfere. | 856 | // borrow); so don't check if they interfere. |
848 | // | 857 | // |
849 | // NOTE: *reservations* do conflict with themselves; | 858 | // NOTE: *reservations* do conflict with themselves; |
850 | // thus aren't injecting unsoundenss w/ this check.) | 859 | // thus aren't injecting unsoundenss w/ this check.) |
851 | (Activation(_, activating), _) if activating == index.borrow_index() => { | 860 | (Activation(_, activating), _) if activating == borrow_index => { |
852 | debug!( | 861 | debug!( |
853 | "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ | 862 | "check_access_for_conflict place_span: {:?} sd: {:?} rw: {:?} \ |
854 | skipping {:?} b/c activation of same borrow_index: {:?}", | 863 | skipping {:?} b/c activation of same borrow_index", |
855 | place_span, | 864 | place_span, |
856 | sd, | 865 | sd, |
857 | rw, | 866 | rw, |
858 | (index, borrow), | 867 | (borrow_index, borrow), |
859 | index.borrow_index() | ||
860 | ); | 868 | ); |
861 | Control::Continue | 869 | Control::Continue |
862 | } | 870 | } |
@@ -867,7 +875,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
867 | 875 | ||
868 | (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => { | 876 | (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => { |
869 | // Reading from mere reservations of mutable-borrows is OK. | 877 | // Reading from mere reservations of mutable-borrows is OK. |
870 | if this.allow_two_phase_borrow(borrow.kind) && index.is_reservation() { | 878 | if !this.is_active(borrow, context.loc) { |
879 | assert!(this.allow_two_phase_borrow(borrow.kind)); | ||
871 | return Control::Continue; | 880 | return Control::Continue; |
872 | } | 881 | } |
873 | 882 | ||
@@ -877,17 +886,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
877 | this.report_use_while_mutably_borrowed(context, place_span, borrow) | 886 | this.report_use_while_mutably_borrowed(context, place_span, borrow) |
878 | } | 887 | } |
879 | ReadKind::Borrow(bk) => { | 888 | ReadKind::Borrow(bk) => { |
880 | let end_issued_loan_span = flow_state | ||
881 | .borrows | ||
882 | .operator() | ||
883 | .opt_region_end_span(&borrow.region); | ||
884 | error_reported = true; | 889 | error_reported = true; |
885 | this.report_conflicting_borrow( | 890 | this.report_conflicting_borrow( |
886 | context, | 891 | context, |
887 | place_span, | 892 | place_span, |
888 | bk, | 893 | bk, |
889 | &borrow, | 894 | &borrow, |
890 | end_issued_loan_span, | ||
891 | ) | 895 | ) |
892 | } | 896 | } |
893 | } | 897 | } |
@@ -919,18 +923,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
919 | 923 | ||
920 | match kind { | 924 | match kind { |
921 | WriteKind::MutableBorrow(bk) => { | 925 | WriteKind::MutableBorrow(bk) => { |
922 | let end_issued_loan_span = flow_state | ||
923 | .borrows | ||
924 | .operator() | ||
925 | .opt_region_end_span(&borrow.region); | ||
926 | |||
927 | error_reported = true; | 926 | error_reported = true; |
928 | this.report_conflicting_borrow( | 927 | this.report_conflicting_borrow( |
929 | context, | 928 | context, |
930 | place_span, | 929 | place_span, |
931 | bk, | 930 | bk, |
932 | &borrow, | 931 | &borrow, |
933 | end_issued_loan_span, | ||
934 | ) | 932 | ) |
935 | } | 933 | } |
936 | WriteKind::StorageDeadOrDrop => { | 934 | WriteKind::StorageDeadOrDrop => { |
@@ -939,7 +937,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
939 | context, | 937 | context, |
940 | borrow, | 938 | borrow, |
941 | place_span.1, | 939 | place_span.1, |
942 | flow_state.borrows.operator(), | ||
943 | ); | 940 | ); |
944 | } | 941 | } |
945 | WriteKind::Mutate => { | 942 | WriteKind::Mutate => { |
@@ -1141,7 +1138,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
1141 | context: Context, | 1138 | context: Context, |
1142 | borrow: &BorrowData<'tcx>, | 1139 | borrow: &BorrowData<'tcx>, |
1143 | span: Span, | 1140 | span: Span, |
1144 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | ||
1145 | ) { | 1141 | ) { |
1146 | debug!("check_for_invalidation_at_exit({:?})", borrow); | 1142 | debug!("check_for_invalidation_at_exit({:?})", borrow); |
1147 | let place = &borrow.borrowed_place; | 1143 | let place = &borrow.borrowed_place; |
@@ -1194,7 +1190,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
1194 | context, | 1190 | context, |
1195 | borrow, | 1191 | borrow, |
1196 | span, | 1192 | span, |
1197 | flow_state.borrows.operator(), | ||
1198 | ) | 1193 | ) |
1199 | } | 1194 | } |
1200 | } | 1195 | } |
@@ -1249,36 +1244,30 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
1249 | // Two-phase borrow support: For each activation that is newly | 1244 | // Two-phase borrow support: For each activation that is newly |
1250 | // generated at this statement, check if it interferes with | 1245 | // generated at this statement, check if it interferes with |
1251 | // another borrow. | 1246 | // another borrow. |
1252 | let domain = flow_state.borrows.operator(); | 1247 | let borrow_set = self.borrow_set.clone(); |
1253 | let data = domain.borrows(); | 1248 | for &borrow_index in borrow_set.activations_at_location(location) { |
1254 | flow_state.borrows.each_gen_bit(|gen| { | 1249 | let borrow = &borrow_set[borrow_index]; |
1255 | if gen.is_activation() { | 1250 | |
1256 | let borrow_index = gen.borrow_index(); | 1251 | // only mutable borrows should be 2-phase |
1257 | let borrow = &data[borrow_index]; | 1252 | assert!(match borrow.kind { |
1258 | // currently the flow analysis registers | 1253 | BorrowKind::Shared => false, |
1259 | // activations for both mutable and immutable | 1254 | BorrowKind::Unique | BorrowKind::Mut { .. } => true, |
1260 | // borrows. So make sure we are talking about a | 1255 | }); |
1261 | // mutable borrow before we check it. | 1256 | |
1262 | match borrow.kind { | 1257 | self.access_place( |
1263 | BorrowKind::Shared => return, | 1258 | ContextKind::Activation.new(location), |
1264 | BorrowKind::Unique | BorrowKind::Mut { .. } => {} | 1259 | (&borrow.borrowed_place, span), |
1265 | } | 1260 | ( |
1266 | 1261 | Deep, | |
1267 | self.access_place( | 1262 | Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index), |
1268 | ContextKind::Activation.new(location), | 1263 | ), |
1269 | (&borrow.borrowed_place, span), | 1264 | LocalMutationIsAllowed::No, |
1270 | ( | 1265 | flow_state, |
1271 | Deep, | 1266 | ); |
1272 | Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index), | 1267 | // We do not need to call `check_if_path_or_subpath_is_moved` |
1273 | ), | 1268 | // again, as we already called it when we made the |
1274 | LocalMutationIsAllowed::No, | 1269 | // initial reservation. |
1275 | flow_state, | 1270 | } |
1276 | ); | ||
1277 | // We do not need to call `check_if_path_or_subpath_is_moved` | ||
1278 | // again, as we already called it when we made the | ||
1279 | // initial reservation. | ||
1280 | } | ||
1281 | }); | ||
1282 | } | 1271 | } |
1283 | } | 1272 | } |
1284 | 1273 | ||
@@ -2217,18 +2206,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
2217 | unreachable!("iter::repeat returned None") | 2206 | unreachable!("iter::repeat returned None") |
2218 | } | 2207 | } |
2219 | 2208 | ||
2220 | /// This function iterates over all of the current borrows | 2209 | /// This function iterates over all of the in-scope borrows that |
2221 | /// (represented by 1-bits in `flow_state.borrows`) that conflict | 2210 | /// conflict with an access to a place, invoking the `op` callback |
2222 | /// with an access to a place, invoking the `op` callback for each | 2211 | /// for each one. |
2223 | /// one. | ||
2224 | /// | 2212 | /// |
2225 | /// "Current borrow" here means a borrow that reaches the point in | 2213 | /// "Current borrow" here means a borrow that reaches the point in |
2226 | /// the control-flow where the access occurs. | 2214 | /// the control-flow where the access occurs. |
2227 | /// | 2215 | /// |
2228 | /// The borrow's phase is represented by the ReserveOrActivateIndex | 2216 | /// The borrow's phase is represented by the IsActive parameter |
2229 | /// passed to the callback: one can call `is_reservation()` and | 2217 | /// passed to the callback. |
2230 | /// `is_activation()` to determine what phase the borrow is | ||
2231 | /// currently in, when such distinction matters. | ||
2232 | fn each_borrow_involving_path<F>( | 2218 | fn each_borrow_involving_path<F>( |
2233 | &mut self, | 2219 | &mut self, |
2234 | _context: Context, | 2220 | _context: Context, |
@@ -2236,20 +2222,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
2236 | flow_state: &Flows<'cx, 'gcx, 'tcx>, | 2222 | flow_state: &Flows<'cx, 'gcx, 'tcx>, |
2237 | mut op: F, | 2223 | mut op: F, |
2238 | ) where | 2224 | ) where |
2239 | F: FnMut(&mut Self, ReserveOrActivateIndex, &BorrowData<'tcx>) -> Control, | 2225 | F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>) -> Control, |
2240 | { | 2226 | { |
2241 | let (access, place) = access_place; | 2227 | let (access, place) = access_place; |
2242 | 2228 | ||
2243 | // FIXME: analogous code in check_loans first maps `place` to | 2229 | // FIXME: analogous code in check_loans first maps `place` to |
2244 | // its base_path. | 2230 | // its base_path. |
2245 | 2231 | ||
2246 | let data = flow_state.borrows.operator().borrows(); | ||
2247 | |||
2248 | // check for loan restricting path P being used. Accounts for | 2232 | // check for loan restricting path P being used. Accounts for |
2249 | // borrows of P, P.a.b, etc. | 2233 | // borrows of P, P.a.b, etc. |
2250 | let mut iter_incoming = flow_state.borrows.iter_incoming(); | 2234 | let borrow_set = self.borrow_set.clone(); |
2251 | while let Some(i) = iter_incoming.next() { | 2235 | for i in flow_state.borrows_in_scope() { |
2252 | let borrowed = &data[i.borrow_index()]; | 2236 | let borrowed = &borrow_set[i]; |
2253 | 2237 | ||
2254 | if self.places_conflict(&borrowed.borrowed_place, place, access) { | 2238 | if self.places_conflict(&borrowed.borrowed_place, place, access) { |
2255 | debug!( | 2239 | debug!( |
@@ -2263,6 +2247,65 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | |||
2263 | } | 2247 | } |
2264 | } | 2248 | } |
2265 | } | 2249 | } |
2250 | |||
2251 | fn is_active( | ||
2252 | &self, | ||
2253 | borrow_data: &BorrowData<'tcx>, | ||
2254 | location: Location | ||
2255 | ) -> bool { | ||
2256 | debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location); | ||
2257 | |||
2258 | // If this is not a 2-phase borrow, it is always active. | ||
2259 | let activation_location = match borrow_data.activation_location { | ||
2260 | Some(v) => v, | ||
2261 | None => return true, | ||
2262 | }; | ||
2263 | |||
2264 | // Otherwise, it is active for every location *except* in between | ||
2265 | // the reservation and the activation: | ||
2266 | // | ||
2267 | // X | ||
2268 | // / | ||
2269 | // R <--+ Except for this | ||
2270 | // / \ | diamond | ||
2271 | // \ / | | ||
2272 | // A <------+ | ||
2273 | // | | ||
2274 | // Z | ||
2275 | // | ||
2276 | // Note that we assume that: | ||
2277 | // - the reservation R dominates the activation A | ||
2278 | // - the activation A post-dominates the reservation R (ignoring unwinding edges). | ||
2279 | // | ||
2280 | // This means that there can't be an edge that leaves A and | ||
2281 | // comes back into that diamond unless it passes through R. | ||
2282 | // | ||
2283 | // Suboptimal: In some cases, this code walks the dominator | ||
2284 | // tree twice when it only has to be walked once. I am | ||
2285 | // lazy. -nmatsakis | ||
2286 | |||
2287 | // If dominated by the activation A, then it is active. The | ||
2288 | // activation occurs upon entering the point A, so this is | ||
2289 | // also true if location == activation_location. | ||
2290 | if activation_location.dominates(location, &self.dominators) { | ||
2291 | return true; | ||
2292 | } | ||
2293 | |||
2294 | // The reservation starts *on exiting* the reservation block, | ||
2295 | // so check if the location is dominated by R.successor. If so, | ||
2296 | // this point falls in between the reservation and location. | ||
2297 | let reserve_location = borrow_data.reserve_location.successor_within_block(); | ||
2298 | if reserve_location.dominates(location, &self.dominators) { | ||
2299 | false | ||
2300 | } else { | ||
2301 | // Otherwise, this point is outside the diamond, so | ||
2302 | // consider the borrow active. This could happen for | ||
2303 | // example if the borrow remains active around a loop (in | ||
2304 | // which case it would be active also for the point R, | ||
2305 | // which would generate an error). | ||
2306 | true | ||
2307 | } | ||
2308 | } | ||
2266 | } | 2309 | } |
2267 | 2310 | ||
2268 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { | 2311 | impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> { |