summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2018-05-11 23:32:13 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2018-05-29 23:02:40 +0200
commit3bc5073dbb9e284a64ae8815bdb54d3b1d6c484a (patch)
tree0e32581b8efd03bb5ccb8255c9a11c6e1e0d29ec
parentFallout from allowing some mutation in guards. (diff)
downloadgrust-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.rs44
-rw-r--r--src/librustc_mir/borrow_check/path_utils.rs9
-rw-r--r--src/librustc_mir/build/expr/as_place.rs5
-rw-r--r--src/librustc_mir/build/matches/mod.rs183
-rw-r--r--src/librustc_mir/build/mod.rs21
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)]
62crate enum TwoPhaseUse {
63 MutActivate,
64 SharedUse,
65}
66
56#[derive(Debug)] 67#[derive(Debug)]
57crate struct BorrowData<'tcx> { 68crate 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.
14use borrow_check::ArtificialField; 14use borrow_check::ArtificialField;
15use borrow_check::borrow_set::{BorrowSet, BorrowData}; 15use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseUse};
16use borrow_check::{Context, Overlap}; 16use borrow_check::{Context, Overlap};
17use borrow_check::{ShallowOrDeep, Deep, Shallow}; 17use borrow_check::{ShallowOrDeep, Deep, Shallow};
18use dataflow::indexes::BorrowIndex; 18use 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
13use build::{BlockAnd, BlockAndExtension, Builder}; 13use build::{BlockAnd, BlockAndExtension, Builder};
14use build::ForGuard::{OutsideGuard, WithinGuard}; 14use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard};
15use build::expr::category::Category; 15use build::expr::category::Category;
16use hair::*; 16use hair::*;
17use rustc::mir::*; 17use 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
16use build::{BlockAnd, BlockAndExtension, Builder}; 16use build::{BlockAnd, BlockAndExtension, Builder};
17use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; 17use build::{GuardFrame, GuardFrameLocal, LocalsForNode};
18use build::ForGuard::{self, OutsideGuard, WithinGuard}; 18use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
19use rustc_data_structures::fx::FxHashMap; 19use rustc_data_structures::fx::FxHashMap;
20use rustc_data_structures::bitvec::BitVector; 20use rustc_data_structures::bitvec::BitVector;
21use rustc::ty::{self, Ty}; 21use 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)]
294enum LocalsForNode { 294enum 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)]
332enum ForGuard { 334enum 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 }