diff options
author | Felix S. Klock II <pnkfelix@pnkfx.org> | 2018-05-11 23:32:13 +0200 |
---|---|---|
committer | Felix S. Klock II <pnkfelix@pnkfx.org> | 2018-05-29 23:02:40 +0200 |
commit | 3bc5073dbb9e284a64ae8815bdb54d3b1d6c484a (patch) | |
tree | 0e32581b8efd03bb5ccb8255c9a11c6e1e0d29ec | |
parent | Fallout from allowing some mutation in guards. (diff) | |
download | grust-3bc5073dbb9e284a64ae8815bdb54d3b1d6c484a.tar.gz grust-3bc5073dbb9e284a64ae8815bdb54d3b1d6c484a.tar.bz2 grust-3bc5073dbb9e284a64ae8815bdb54d3b1d6c484a.tar.xz |
Expand two-phase-borrows so that a case like this still compiles:
```rust
fn main() {
fn reuse<X>(_: &mut X) {}
let mut t = 2f64;
match t {
ref mut _b if { false } => { reuse(_b); }
_ => {}
}
}
```
Note: The way this is currently written is confusing; when `autoref`
is off, then the arm body bindings (introduced by
`bind_matched_candidate_for_arm_body`) are *also* used for the guard.
(Any attempt to fix this needs to still ensure that the bindings used
by the guard are introduced before the guard is evaluated.)
(Once we turn NLL on by default, we can presumably simplify all of
that.)
-rw-r--r-- | src/librustc_mir/borrow_check/borrow_set.rs | 44 | ||||
-rw-r--r-- | src/librustc_mir/borrow_check/path_utils.rs | 9 | ||||
-rw-r--r-- | src/librustc_mir/build/expr/as_place.rs | 5 | ||||
-rw-r--r-- | src/librustc_mir/build/matches/mod.rs | 183 | ||||
-rw-r--r-- | src/librustc_mir/build/mod.rs | 21 |
5 files changed, 149 insertions, 113 deletions
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index ccfb44a8b5..5d0913e8eb 100644 --- a/src/librustc_mir/borrow_check/borrow_set.rs +++ b/src/librustc_mir/borrow_check/borrow_set.rs | |||
@@ -53,6 +53,17 @@ impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> { | |||
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
56 | /// Every two-phase borrow has *exactly one* use (or else it is not a | ||
57 | /// proper two-phase borrow under our current definition. However, not | ||
58 | /// all uses are actually ones that activate the reservation.. In | ||
59 | /// particular, a shared borrow of a `&mut` does not activate the | ||
60 | /// reservation. | ||
61 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
62 | crate enum TwoPhaseUse { | ||
63 | MutActivate, | ||
64 | SharedUse, | ||
65 | } | ||
66 | |||
56 | #[derive(Debug)] | 67 | #[derive(Debug)] |
57 | crate struct BorrowData<'tcx> { | 68 | crate struct BorrowData<'tcx> { |
58 | /// Location where the borrow reservation starts. | 69 | /// Location where the borrow reservation starts. |
@@ -60,7 +71,7 @@ crate struct BorrowData<'tcx> { | |||
60 | crate reserve_location: Location, | 71 | crate reserve_location: Location, |
61 | /// Location where the borrow is activated. None if this is not a | 72 | /// Location where the borrow is activated. None if this is not a |
62 | /// 2-phase borrow. | 73 | /// 2-phase borrow. |
63 | crate activation_location: Option<Location>, | 74 | crate activation_location: Option<(TwoPhaseUse, Location)>, |
64 | /// What kind of borrow this is | 75 | /// What kind of borrow this is |
65 | crate kind: mir::BorrowKind, | 76 | crate kind: mir::BorrowKind, |
66 | /// The region for which this borrow is live | 77 | /// The region for which this borrow is live |
@@ -215,9 +226,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { | |||
215 | Some(&borrow_index) => { | 226 | Some(&borrow_index) => { |
216 | let borrow_data = &mut self.idx_vec[borrow_index]; | 227 | let borrow_data = &mut self.idx_vec[borrow_index]; |
217 | 228 | ||
218 | // Watch out: the use of TMP in the borrow | 229 | // Watch out: the use of TMP in the borrow itself |
219 | // itself doesn't count as an | 230 | // doesn't count as an activation. =) |
220 | // activation. =) | ||
221 | if borrow_data.reserve_location == location && context == PlaceContext::Store { | 231 | if borrow_data.reserve_location == location && context == PlaceContext::Store { |
222 | return; | 232 | return; |
223 | } | 233 | } |
@@ -225,7 +235,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { | |||
225 | if let Some(other_activation) = borrow_data.activation_location { | 235 | if let Some(other_activation) = borrow_data.activation_location { |
226 | span_bug!( | 236 | span_bug!( |
227 | self.mir.source_info(location).span, | 237 | self.mir.source_info(location).span, |
228 | "found two activations for 2-phase borrow temporary {:?}: \ | 238 | "found two uses for 2-phase borrow temporary {:?}: \ |
229 | {:?} and {:?}", | 239 | {:?} and {:?}", |
230 | temp, | 240 | temp, |
231 | location, | 241 | location, |
@@ -235,11 +245,25 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> { | |||
235 | 245 | ||
236 | // Otherwise, this is the unique later use | 246 | // Otherwise, this is the unique later use |
237 | // that we expect. | 247 | // that we expect. |
238 | borrow_data.activation_location = Some(location); | 248 | |
239 | self.activation_map | 249 | let two_phase_use; |
240 | .entry(location) | 250 | |
241 | .or_insert(Vec::new()) | 251 | match context { |
242 | .push(borrow_index); | 252 | // The use of TMP in a shared borrow does not |
253 | // count as an actual activation. | ||
254 | PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. } => { | ||
255 | two_phase_use = TwoPhaseUse::SharedUse; | ||
256 | } | ||
257 | _ => { | ||
258 | two_phase_use = TwoPhaseUse::MutActivate; | ||
259 | self.activation_map | ||
260 | .entry(location) | ||
261 | .or_insert(Vec::new()) | ||
262 | .push(borrow_index); | ||
263 | } | ||
264 | } | ||
265 | |||
266 | borrow_data.activation_location = Some((two_phase_use, location)); | ||
243 | } | 267 | } |
244 | 268 | ||
245 | None => {} | 269 | None => {} |
diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index d8d160b73e..4871d427d0 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs | |||
@@ -12,7 +12,7 @@ | |||
12 | /// allowed to be split into separate Reservation and | 12 | /// allowed to be split into separate Reservation and |
13 | /// Activation phases. | 13 | /// Activation phases. |
14 | use borrow_check::ArtificialField; | 14 | use borrow_check::ArtificialField; |
15 | use borrow_check::borrow_set::{BorrowSet, BorrowData}; | 15 | use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseUse}; |
16 | use borrow_check::{Context, Overlap}; | 16 | use borrow_check::{Context, Overlap}; |
17 | use borrow_check::{ShallowOrDeep, Deep, Shallow}; | 17 | use borrow_check::{ShallowOrDeep, Deep, Shallow}; |
18 | use dataflow::indexes::BorrowIndex; | 18 | use dataflow::indexes::BorrowIndex; |
@@ -431,10 +431,13 @@ pub(super) fn is_active<'tcx>( | |||
431 | ) -> bool { | 431 | ) -> bool { |
432 | debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location); | 432 | debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location); |
433 | 433 | ||
434 | // If this is not a 2-phase borrow, it is always active. | ||
435 | let activation_location = match borrow_data.activation_location { | 434 | let activation_location = match borrow_data.activation_location { |
436 | Some(v) => v, | 435 | // If this is not a 2-phase borrow, it is always active. |
437 | None => return true, | 436 | None => return true, |
437 | // And if the unique 2-phase use is not an activation, then it is *never* active. | ||
438 | Some((TwoPhaseUse::SharedUse, _)) => return false, | ||
439 | // Otherwise, we derive info from the activation point `v`: | ||
440 | Some((TwoPhaseUse::MutActivate, v)) => v, | ||
438 | }; | 441 | }; |
439 | 442 | ||
440 | // Otherwise, it is active for every location *except* in between | 443 | // Otherwise, it is active for every location *except* in between |
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index 365b9babd0..964841e7a9 100644 --- a/src/librustc_mir/build/expr/as_place.rs +++ b/src/librustc_mir/build/expr/as_place.rs | |||
@@ -11,7 +11,7 @@ | |||
11 | //! See docs in build/expr/mod.rs | 11 | //! See docs in build/expr/mod.rs |
12 | 12 | ||
13 | use build::{BlockAnd, BlockAndExtension, Builder}; | 13 | use build::{BlockAnd, BlockAndExtension, Builder}; |
14 | use build::ForGuard::{OutsideGuard, WithinGuard}; | 14 | use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard}; |
15 | use build::expr::category::Category; | 15 | use build::expr::category::Category; |
16 | use hair::*; | 16 | use hair::*; |
17 | use rustc::mir::*; | 17 | use rustc::mir::*; |
@@ -88,10 +88,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
88 | } | 88 | } |
89 | ExprKind::VarRef { id } => { | 89 | ExprKind::VarRef { id } => { |
90 | let place = if this.is_bound_var_in_guard(id) { | 90 | let place = if this.is_bound_var_in_guard(id) { |
91 | let index = this.var_local_id(id, WithinGuard); | ||
92 | if this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() { | 91 | if this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() { |
92 | let index = this.var_local_id(id, RefWithinGuard); | ||
93 | Place::Local(index).deref() | 93 | Place::Local(index).deref() |
94 | } else { | 94 | } else { |
95 | let index = this.var_local_id(id, ValWithinGuard); | ||
95 | Place::Local(index) | 96 | Place::Local(index) |
96 | } | 97 | } |
97 | } else { | 98 | } else { |
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 67c9db1bf9..552066e679 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs | |||
@@ -15,7 +15,7 @@ | |||
15 | 15 | ||
16 | use build::{BlockAnd, BlockAndExtension, Builder}; | 16 | use build::{BlockAnd, BlockAndExtension, Builder}; |
17 | use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; | 17 | use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; |
18 | use build::ForGuard::{self, OutsideGuard, WithinGuard}; | 18 | use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard}; |
19 | use rustc_data_structures::fx::FxHashMap; | 19 | use rustc_data_structures::fx::FxHashMap; |
20 | use rustc_data_structures::bitvec::BitVector; | 20 | use rustc_data_structures::bitvec::BitVector; |
21 | use rustc::ty::{self, Ty}; | 21 | use rustc::ty::{self, Ty}; |
@@ -88,12 +88,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
88 | 88 | ||
89 | let mut arm_blocks = ArmBlocks { | 89 | let mut arm_blocks = ArmBlocks { |
90 | blocks: arms.iter() | 90 | blocks: arms.iter() |
91 | .map(|_| { | 91 | .map(|_| self.cfg.start_new_block()) |
92 | let arm_block = self.cfg.start_new_block(); | 92 | .collect(), |
93 | arm_block | ||
94 | }) | ||
95 | .collect(), | ||
96 | |||
97 | }; | 93 | }; |
98 | 94 | ||
99 | // Get the arm bodies and their scopes, while declaring bindings. | 95 | // Get the arm bodies and their scopes, while declaring bindings. |
@@ -110,9 +106,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
110 | // create binding start block for link them by false edges | 106 | // create binding start block for link them by false edges |
111 | let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len()); | 107 | let candidate_count = arms.iter().fold(0, |ac, c| ac + c.patterns.len()); |
112 | let pre_binding_blocks: Vec<_> = (0..candidate_count + 1) | 108 | let pre_binding_blocks: Vec<_> = (0..candidate_count + 1) |
113 | .map(|_| self.cfg.start_new_block()) | 109 | .map(|_| self.cfg.start_new_block()).collect(); |
114 | |||
115 | .collect(); | ||
116 | 110 | ||
117 | // assemble a list of candidates: there is one candidate per | 111 | // assemble a list of candidates: there is one candidate per |
118 | // pattern, which means there may be more than one candidate | 112 | // pattern, which means there may be more than one candidate |
@@ -315,7 +309,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
315 | } | 309 | } |
316 | 310 | ||
317 | // now apply the bindings, which will also declare the variables | 311 | // now apply the bindings, which will also declare the variables |
318 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); | 312 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); |
319 | 313 | ||
320 | block.unit() | 314 | block.unit() |
321 | } | 315 | } |
@@ -956,22 +950,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
956 | // (because all we have is the places associated with the | 950 | // (because all we have is the places associated with the |
957 | // match input itself; it is up to us to create a place | 951 | // match input itself; it is up to us to create a place |
958 | // holding a `&` or `&mut` that we can then borrow). | 952 | // holding a `&` or `&mut` that we can then borrow). |
959 | // | ||
960 | // * Therefore, when the binding is by-reference, we | ||
961 | // *eagerly* introduce the binding for the arm body | ||
962 | // (`tmp2`) and then borrow it (`tmp1`). | ||
963 | // | ||
964 | // * This is documented with "NOTE tricky business" below. | ||
965 | // | ||
966 | // FIXME The distinction in how `tmp2` is initialized is | ||
967 | // currently encoded in a pretty awkward fashion; namely, by | ||
968 | // passing a boolean to bind_matched_candidate_for_arm_body | ||
969 | // indicating whether all of the by-ref bindings were already | ||
970 | // initialized. | ||
971 | // | ||
972 | // * Also: pnkfelix thinks "laziness" is natural; but since | ||
973 | // MIR-borrowck did not complain with earlier (universally | ||
974 | // eager) MIR codegen, laziness might not be *necessary*. | ||
975 | 953 | ||
976 | let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards(); | 954 | let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards(); |
977 | if let Some(guard) = candidate.guard { | 955 | if let Some(guard) = candidate.guard { |
@@ -985,7 +963,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
985 | debug!("Entering guard building context: {:?}", guard_frame); | 963 | debug!("Entering guard building context: {:?}", guard_frame); |
986 | self.guard_context.push(guard_frame); | 964 | self.guard_context.push(guard_frame); |
987 | } else { | 965 | } else { |
988 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); | 966 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); |
989 | } | 967 | } |
990 | 968 | ||
991 | // the block to branch to if the guard fails; if there is no | 969 | // the block to branch to if the guard fails; if there is no |
@@ -999,14 +977,47 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
999 | } | 977 | } |
1000 | 978 | ||
1001 | let false_edge_block = self.cfg.start_new_block(); | 979 | let false_edge_block = self.cfg.start_new_block(); |
980 | |||
981 | // We want to ensure that the matched candidates are bound | ||
982 | // after we have confirmed this candidate *and* any | ||
983 | // associated guard; Binding them on `block` is too soon, | ||
984 | // because that would be before we've checked the result | ||
985 | // from the guard. | ||
986 | // | ||
987 | // But binding them on `arm_block` is *too late*, because | ||
988 | // then all of the candidates for a single arm would be | ||
989 | // bound in the same place, that would cause a case like: | ||
990 | // | ||
991 | // ```rust | ||
992 | // match (30, 2) { | ||
993 | // (mut x, 1) | (2, mut x) if { true } => { ... } | ||
994 | // ... // ^^^^^^^ (this is `arm_block`) | ||
995 | // } | ||
996 | // ``` | ||
997 | // | ||
998 | // would yield a `arm_block` something like: | ||
999 | // | ||
1000 | // ``` | ||
1001 | // StorageLive(_4); // _4 is `x` | ||
1002 | // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case | ||
1003 | // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case | ||
1004 | // ``` | ||
1005 | // | ||
1006 | // and that is clearly not correct. | ||
1007 | let post_guard_block = self.cfg.start_new_block(); | ||
1002 | self.cfg.terminate(block, source_info, | 1008 | self.cfg.terminate(block, source_info, |
1003 | TerminatorKind::if_(self.hir.tcx(), cond, arm_block, | 1009 | TerminatorKind::if_(self.hir.tcx(), cond, post_guard_block, |
1004 | false_edge_block)); | 1010 | false_edge_block)); |
1005 | 1011 | ||
1006 | let otherwise = self.cfg.start_new_block(); | ||
1007 | if autoref { | 1012 | if autoref { |
1008 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, true); | 1013 | self.bind_matched_candidate_for_arm_body(post_guard_block, &candidate.bindings); |
1009 | } | 1014 | } |
1015 | |||
1016 | self.cfg.terminate(post_guard_block, source_info, | ||
1017 | TerminatorKind::Goto { target: arm_block }); | ||
1018 | |||
1019 | let otherwise = self.cfg.start_new_block(); | ||
1020 | |||
1010 | self.cfg.terminate(false_edge_block, source_info, | 1021 | self.cfg.terminate(false_edge_block, source_info, |
1011 | TerminatorKind::FalseEdges { | 1022 | TerminatorKind::FalseEdges { |
1012 | real_target: otherwise, | 1023 | real_target: otherwise, |
@@ -1015,13 +1026,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
1015 | }); | 1026 | }); |
1016 | Some(otherwise) | 1027 | Some(otherwise) |
1017 | } else { | 1028 | } else { |
1018 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); | 1029 | // (Here, it is not too early to bind the matched |
1030 | // candidate on `block`, because there is no guard result | ||
1031 | // that we have to inspect before we bind them.) | ||
1032 | self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); | ||
1019 | self.cfg.terminate(block, candidate_source_info, | 1033 | self.cfg.terminate(block, candidate_source_info, |
1020 | TerminatorKind::Goto { target: arm_block }); | 1034 | TerminatorKind::Goto { target: arm_block }); |
1021 | None | 1035 | None |
1022 | } | 1036 | } |
1023 | } | 1037 | } |
1024 | 1038 | ||
1039 | // Only called when all_pat_vars_are_implicit_refs_within_guards, | ||
1040 | // and thus all code/comments assume we are in that context. | ||
1025 | fn bind_matched_candidate_for_guard(&mut self, | 1041 | fn bind_matched_candidate_for_guard(&mut self, |
1026 | block: BasicBlock, | 1042 | block: BasicBlock, |
1027 | bindings: &[Binding<'tcx>]) { | 1043 | bindings: &[Binding<'tcx>]) { |
@@ -1034,61 +1050,54 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
1034 | let re_empty = self.hir.tcx().types.re_empty; | 1050 | let re_empty = self.hir.tcx().types.re_empty; |
1035 | for binding in bindings { | 1051 | for binding in bindings { |
1036 | let source_info = self.source_info(binding.span); | 1052 | let source_info = self.source_info(binding.span); |
1037 | let local_for_guard = self.storage_live_binding( | 1053 | |
1038 | block, binding.var_id, binding.span, WithinGuard); | 1054 | // For each pattern ident P of type T, `ref_for_guard` is |
1055 | // a reference R: &T pointing to the location matched by | ||
1056 | // the pattern, and every occurrence of P within a guard | ||
1057 | // denotes *R. | ||
1058 | let ref_for_guard = self.storage_live_binding( | ||
1059 | block, binding.var_id, binding.span, RefWithinGuard); | ||
1039 | // Question: Why schedule drops if bindings are all | 1060 | // Question: Why schedule drops if bindings are all |
1040 | // shared-&'s? Answer: Because schedule_drop_for_binding | 1061 | // shared-&'s? Answer: Because schedule_drop_for_binding |
1041 | // also emits StorageDead's for those locals. | 1062 | // also emits StorageDead's for those locals. |
1042 | self.schedule_drop_for_binding(binding.var_id, binding.span, WithinGuard); | 1063 | self.schedule_drop_for_binding(binding.var_id, binding.span, RefWithinGuard); |
1043 | match binding.binding_mode { | 1064 | match binding.binding_mode { |
1044 | BindingMode::ByValue => { | 1065 | BindingMode::ByValue => { |
1045 | let rvalue = Rvalue::Ref(re_empty, BorrowKind::Shared, binding.source.clone()); | 1066 | let rvalue = Rvalue::Ref(re_empty, BorrowKind::Shared, binding.source.clone()); |
1046 | self.cfg.push_assign(block, source_info, &local_for_guard, rvalue); | 1067 | self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue); |
1047 | } | 1068 | } |
1048 | BindingMode::ByRef(region, borrow_kind) => { | 1069 | BindingMode::ByRef(region, borrow_kind) => { |
1049 | // NOTE tricky business: For `ref id` and `ref mut | 1070 | // Tricky business: For `ref id` and `ref mut id` |
1050 | // id` patterns, we want `id` within the guard to | 1071 | // patterns, we want `id` within the guard to |
1051 | // correspond to a temp of type `& &T` or `& &mut | 1072 | // correspond to a temp of type `& &T` or `& &mut |
1052 | // T`, while within the arm body it will | 1073 | // T` (i.e. a "borrow of a borrow") that is |
1053 | // correspond to a temp of type `&T` or `&mut T`, | 1074 | // implicitly dereferenced. |
1054 | // as usual. | ||
1055 | // | ||
1056 | // But to inject the level of indirection, we need | ||
1057 | // something to point to. | ||
1058 | // | 1075 | // |
1059 | // So: | 1076 | // To borrow a borrow, we need that inner borrow |
1077 | // to point to. So, create a temp for the inner | ||
1078 | // borrow, and then take a reference to it. | ||
1060 | // | 1079 | // |
1061 | // 1. First set up the local for the arm body | 1080 | // Note: the temp created here is *not* the one |
1062 | // (even though we have not yet evaluated the | 1081 | // used by the arm body itself. This eases |
1063 | // guard itself), | 1082 | // observing two-phase borrow restrictions. |
1064 | // | 1083 | let val_for_guard = self.storage_live_binding( |
1065 | // 2. Then setup the local for the guard, which is | 1084 | block, binding.var_id, binding.span, ValWithinGuard); |
1066 | // just a reference to the local from step 1. | 1085 | self.schedule_drop_for_binding(binding.var_id, binding.span, ValWithinGuard); |
1067 | // | 1086 | |
1068 | // Note that since we are setting up the local for | 1087 | // rust-lang/rust#27282: We reuse the two-phase |
1069 | // the arm body a bit eagerly here (and likewise | 1088 | // borrow infrastructure so that the mutable |
1070 | // scheduling its drop code), we should *not* do | 1089 | // borrow (whose mutabilty is *unusable* within |
1071 | // it redundantly later on. | 1090 | // the guard) does not conflict with the implicit |
1072 | // | 1091 | // borrow of the whole match input. See additional |
1073 | // While we could have kept track of this with a | 1092 | // discussion on rust-lang/rust#49870. |
1074 | // flag or collection of such bindings, the | 1093 | let borrow_kind = match borrow_kind { |
1075 | // treatment of all of these cases is uniform, so | 1094 | BorrowKind::Shared | BorrowKind::Unique => borrow_kind, |
1076 | // we should be safe just avoiding the code | 1095 | BorrowKind::Mut { .. } => BorrowKind::Mut { allow_two_phase_borrow: true }, |
1077 | // without maintaining such state.) | 1096 | }; |
1078 | let local_for_arm_body = self.storage_live_binding( | ||
1079 | block, binding.var_id, binding.span, OutsideGuard); | ||
1080 | self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); | ||
1081 | |||
1082 | // rust-lang/rust#27282: this potentially mutable | ||
1083 | // borrow may require a cast in the future to | ||
1084 | // avoid conflicting with an implicit borrow of | ||
1085 | // the whole match input; or maybe it just | ||
1086 | // requires an extension of our two-phase borrows | ||
1087 | // system. See discussion on rust-lang/rust#49870. | ||
1088 | let rvalue = Rvalue::Ref(region, borrow_kind, binding.source.clone()); | 1097 | let rvalue = Rvalue::Ref(region, borrow_kind, binding.source.clone()); |
1089 | self.cfg.push_assign(block, source_info, &local_for_arm_body, rvalue); | 1098 | self.cfg.push_assign(block, source_info, &val_for_guard, rvalue); |
1090 | let rvalue = Rvalue::Ref(region, BorrowKind::Shared, local_for_arm_body); | 1099 | let rvalue = Rvalue::Ref(region, BorrowKind::Shared, val_for_guard); |
1091 | self.cfg.push_assign(block, source_info, &local_for_guard, rvalue); | 1100 | self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue); |
1092 | } | 1101 | } |
1093 | } | 1102 | } |
1094 | } | 1103 | } |
@@ -1096,19 +1105,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
1096 | 1105 | ||
1097 | fn bind_matched_candidate_for_arm_body(&mut self, | 1106 | fn bind_matched_candidate_for_arm_body(&mut self, |
1098 | block: BasicBlock, | 1107 | block: BasicBlock, |
1099 | bindings: &[Binding<'tcx>], | 1108 | bindings: &[Binding<'tcx>]) { |
1100 | already_initialized_state_for_refs: bool) { | 1109 | debug!("bind_matched_candidate_for_arm_body(block={:?}, bindings={:?}", block, bindings); |
1101 | debug!("bind_matched_candidate_for_arm_body(block={:?}, bindings={:?}, \ | ||
1102 | already_initialized_state_for_refs={:?})", | ||
1103 | block, bindings, already_initialized_state_for_refs); | ||
1104 | 1110 | ||
1105 | // Assign each of the bindings. This may trigger moves out of the candidate. | 1111 | // Assign each of the bindings. This may trigger moves out of the candidate. |
1106 | for binding in bindings { | 1112 | for binding in bindings { |
1107 | if let BindingMode::ByRef(..) = binding.binding_mode { | ||
1108 | // See "NOTE tricky business" above | ||
1109 | if already_initialized_state_for_refs { continue; } | ||
1110 | } | ||
1111 | |||
1112 | let source_info = self.source_info(binding.span); | 1113 | let source_info = self.source_info(binding.span); |
1113 | let local = self.storage_live_binding(block, binding.var_id, binding.span, | 1114 | let local = self.storage_live_binding(block, binding.var_id, binding.span, |
1114 | OutsideGuard); | 1115 | OutsideGuard); |
@@ -1145,7 +1146,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
1145 | var_id, name, var_ty, source_info, syntactic_scope); | 1146 | var_id, name, var_ty, source_info, syntactic_scope); |
1146 | 1147 | ||
1147 | let tcx = self.hir.tcx(); | 1148 | let tcx = self.hir.tcx(); |
1148 | let for_arm_body = self.local_decls.push(LocalDecl::<'tcx> { | 1149 | let local = LocalDecl::<'tcx> { |
1149 | mutability, | 1150 | mutability, |
1150 | ty: var_ty.clone(), | 1151 | ty: var_ty.clone(), |
1151 | name: Some(name), | 1152 | name: Some(name), |
@@ -1153,9 +1154,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
1153 | syntactic_scope, | 1154 | syntactic_scope, |
1154 | internal: false, | 1155 | internal: false, |
1155 | is_user_variable: true, | 1156 | is_user_variable: true, |
1156 | }); | 1157 | }; |
1158 | let for_arm_body = self.local_decls.push(local.clone()); | ||
1157 | let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() { | 1159 | let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() { |
1158 | let for_guard = self.local_decls.push(LocalDecl::<'tcx> { | 1160 | let val_for_guard = self.local_decls.push(local); |
1161 | let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> { | ||
1159 | mutability, | 1162 | mutability, |
1160 | ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty), | 1163 | ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty), |
1161 | name: Some(name), | 1164 | name: Some(name), |
@@ -1164,7 +1167,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
1164 | internal: false, | 1167 | internal: false, |
1165 | is_user_variable: true, | 1168 | is_user_variable: true, |
1166 | }); | 1169 | }); |
1167 | LocalsForNode::Two { for_guard, for_arm_body } | 1170 | LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body } |
1168 | } else { | 1171 | } else { |
1169 | LocalsForNode::One(for_arm_body) | 1172 | LocalsForNode::One(for_arm_body) |
1170 | }; | 1173 | }; |
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 2da2d3f697..4822b9e4df 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs | |||
@@ -293,7 +293,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { | |||
293 | #[derive(Debug)] | 293 | #[derive(Debug)] |
294 | enum LocalsForNode { | 294 | enum LocalsForNode { |
295 | One(Local), | 295 | One(Local), |
296 | Two { for_guard: Local, for_arm_body: Local }, | 296 | Three { val_for_guard: Local, ref_for_guard: Local, for_arm_body: Local }, |
297 | } | 297 | } |
298 | 298 | ||
299 | #[derive(Debug)] | 299 | #[derive(Debug)] |
@@ -325,12 +325,15 @@ struct GuardFrame { | |||
325 | locals: Vec<GuardFrameLocal>, | 325 | locals: Vec<GuardFrameLocal>, |
326 | } | 326 | } |
327 | 327 | ||
328 | /// ForGuard is isomorphic to a boolean flag. It indicates whether we are | 328 | /// ForGuard indicates whether we are talking about: |
329 | /// talking about the temp for a local binding for a use within a guard expression, | 329 | /// 1. the temp for a local binding used solely within guard expressions, |
330 | /// or a temp for use outside of a guard expressions. | 330 | /// 2. the temp that holds reference to (1.), which is actually what the |
331 | /// guard expressions see, or | ||
332 | /// 3. the temp for use outside of guard expressions. | ||
331 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 333 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
332 | enum ForGuard { | 334 | enum ForGuard { |
333 | WithinGuard, | 335 | ValWithinGuard, |
336 | RefWithinGuard, | ||
334 | OutsideGuard, | 337 | OutsideGuard, |
335 | } | 338 | } |
336 | 339 | ||
@@ -338,11 +341,13 @@ impl LocalsForNode { | |||
338 | fn local_id(&self, for_guard: ForGuard) -> Local { | 341 | fn local_id(&self, for_guard: ForGuard) -> Local { |
339 | match (self, for_guard) { | 342 | match (self, for_guard) { |
340 | (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) | | 343 | (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) | |
341 | (&LocalsForNode::Two { for_guard: local_id, .. }, ForGuard::WithinGuard) | | 344 | (&LocalsForNode::Three { val_for_guard: local_id, .. }, ForGuard::ValWithinGuard) | |
342 | (&LocalsForNode::Two { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => | 345 | (&LocalsForNode::Three { ref_for_guard: local_id, .. }, ForGuard::RefWithinGuard) | |
346 | (&LocalsForNode::Three { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => | ||
343 | local_id, | 347 | local_id, |
344 | 348 | ||
345 | (&LocalsForNode::One(_), ForGuard::WithinGuard) => | 349 | (&LocalsForNode::One(_), ForGuard::ValWithinGuard) | |
350 | (&LocalsForNode::One(_), ForGuard::RefWithinGuard) => | ||
346 | bug!("anything with one local should never be within a guard."), | 351 | bug!("anything with one local should never be within a guard."), |
347 | } | 352 | } |
348 | } | 353 | } |