about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2018-07-26 13:14:12 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2018-07-26 13:15:31 +0200
commit97f3d21c6459c2d56a029ad2468cc9c49dbbd8df (patch)
tree521baa83d95ea05af9edf3fa179fec8601fcafdf
parentc636ded2c759be0dc8fdb9143e06e420ef03ace3 (diff)
downloadrust-97f3d21c6459c2d56a029ad2468cc9c49dbbd8df.tar.gz
rust-97f3d21c6459c2d56a029ad2468cc9c49dbbd8df.zip
Issue #51348: lower `match` so an ident gets a distinct temp *for each* candidate pat.
This required a bit of plumbing to keep track of candidates. But I
took advantage of the hack session to try to improve the docs for the
relevant structs here.

(I also tried to simplify some of the related code in passing.)
-rw-r--r--src/librustc_mir/build/block.rs7
-rw-r--r--src/librustc_mir/build/expr/as_place.rs15
-rw-r--r--src/librustc_mir/build/matches/mod.rs62
-rw-r--r--src/librustc_mir/build/matches/test.rs2
-rw-r--r--src/librustc_mir/build/mod.rs43
5 files changed, 92 insertions, 37 deletions
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index bbbe757e96e..954f9051440 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -126,7 +126,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             None,
                             remainder_span,
                             lint_level,
-                            &pattern,
+                            &[pattern.clone()],
                             ArmHasGuard(false),
                             Some((None, initializer_span)),
                         );
@@ -138,8 +138,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                 })
                             }));
                     } else {
-                        scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
-                                                        ArmHasGuard(false), None);
+                        scope = this.declare_bindings(
+                            None, remainder_span, lint_level, &[pattern.clone()],
+                            ArmHasGuard(false), None);
 
                         // FIXME(#47184): We currently only insert `UserAssertTy` statements for
                         // patterns that are bindings, this is as we do not want to deconstruct
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 964841e7a9e..a38ca7ae5bf 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -11,7 +11,7 @@
 //! See docs in build/expr/mod.rs
 
 use build::{BlockAnd, BlockAndExtension, Builder};
-use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard};
+use build::ForGuard::{OutsideGuard, RefWithinGuard};
 use build::expr::category::Category;
 use hair::*;
 use rustc::mir::*;
@@ -87,14 +87,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block.and(Place::Local(Local::new(1)))
             }
             ExprKind::VarRef { id } => {
-                let place = if this.is_bound_var_in_guard(id) {
-                    if this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() {
-                        let index = this.var_local_id(id, RefWithinGuard);
-                        Place::Local(index).deref()
-                    } else {
-                        let index = this.var_local_id(id, ValWithinGuard);
-                        Place::Local(index)
-                    }
+                let place = if this.is_bound_var_in_guard(id) &&
+                    this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards()
+                {
+                    let index = this.var_local_id(id, RefWithinGuard);
+                    Place::Local(index).deref()
                 } else {
                     let index = this.var_local_id(id, OutsideGuard);
                     Place::Local(index)
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index f1591535fa1..962903f1655 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -97,7 +97,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             let body = self.hir.mirror(arm.body.clone());
             let scope = self.declare_bindings(None, body.span,
                                               LintLevel::Inherited,
-                                              &arm.patterns[0],
+                                              &arm.patterns[..],
                                               ArmHasGuard(arm.guard.is_some()),
                                               Some((Some(&discriminant_place), discriminant_span)));
             (body, scope.unwrap_or(self.source_scope))
@@ -118,11 +118,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             arms.iter()
                 .enumerate()
                 .flat_map(|(arm_index, arm)| {
-                    arm.patterns.iter()
-                                .map(move |pat| (arm_index, pat, arm.guard.clone()))
+                    arm.patterns.iter().enumerate()
+                        .map(move |(pat_index, pat)| {
+                            (arm_index, pat_index, pat, arm.guard.clone())
+                        })
                 })
                 .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
-                .map(|((arm_index, pattern, guard),
+                .map(|((arm_index, pat_index, pattern, guard),
                        (pre_binding_block, next_candidate_pre_binding_block))| {
 
                     if let (true, Some(borrow_temp)) = (tcx.emit_read_for_match(),
@@ -168,6 +170,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         bindings: vec![],
                         guard,
                         arm_index,
+                        pat_index,
                         pre_binding_block: *pre_binding_block,
                         next_candidate_pre_binding_block: *next_candidate_pre_binding_block,
                     }
@@ -277,6 +280,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
             // since we don't call `match_candidates`, next fields is unused
             arm_index: 0,
+            pat_index: 0,
             pre_binding_block: block,
             next_candidate_pre_binding_block: block
         };
@@ -324,14 +328,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                             mut visibility_scope: Option<SourceScope>,
                             scope_span: Span,
                             lint_level: LintLevel,
-                            pattern: &Pattern<'tcx>,
+                            patterns: &[Pattern<'tcx>],
                             has_guard: ArmHasGuard,
                             opt_match_place: Option<(Option<&Place<'tcx>>, Span)>)
                             -> Option<SourceScope> {
         assert!(!(visibility_scope.is_some() && lint_level.is_explicit()),
                 "can't have both a visibility and a lint scope at the same time");
         let mut scope = self.source_scope;
-        self.visit_bindings(pattern, &mut |this, mutability, name, mode, var, span, ty| {
+        let num_patterns = patterns.len();
+        self.visit_bindings(&patterns[0], &mut |this, mutability, name, mode, var, span, ty| {
             if visibility_scope.is_none() {
                 visibility_scope = Some(this.new_source_scope(scope_span,
                                                            LintLevel::Inherited,
@@ -349,8 +354,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 scope,
             };
             let visibility_scope = visibility_scope.unwrap();
-            this.declare_binding(source_info, visibility_scope, mutability, name, mode, var,
-                                 ty, has_guard, opt_match_place.map(|(x, y)| (x.cloned(), y)));
+            this.declare_binding(source_info, visibility_scope, mutability, name, mode,
+                                 num_patterns, var, ty, has_guard,
+                                 opt_match_place.map(|(x, y)| (x.cloned(), y)));
         });
         visibility_scope
     }
@@ -453,6 +459,9 @@ pub struct Candidate<'pat, 'tcx:'pat> {
     // ...and the blocks for add false edges between candidates
     pre_binding_block: BasicBlock,
     next_candidate_pre_binding_block: BasicBlock,
+
+    // This uniquely identifies this candidate *within* the arm.
+    pat_index: usize,
 }
 
 #[derive(Clone, Debug)]
@@ -972,7 +981,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards();
         if let Some(guard) = candidate.guard {
             if autoref {
-                self.bind_matched_candidate_for_guard(block, &candidate.bindings);
+                self.bind_matched_candidate_for_guard(
+                    block, candidate.pat_index, &candidate.bindings);
                 let guard_frame = GuardFrame {
                     locals: candidate.bindings.iter()
                         .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode))
@@ -1058,9 +1068,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     // and thus all code/comments assume we are in that context.
     fn bind_matched_candidate_for_guard(&mut self,
                                         block: BasicBlock,
+                                        pat_index: usize,
                                         bindings: &[Binding<'tcx>]) {
-        debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})",
-               block, bindings);
+        debug!("bind_matched_candidate_for_guard(block={:?}, pat_index={:?}, bindings={:?})",
+               block, pat_index, bindings);
 
         // Assign each of the bindings. Since we are binding for a
         // guard expression, this will never trigger moves out of the
@@ -1099,8 +1110,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     // used by the arm body itself. This eases
                     // observing two-phase borrow restrictions.
                     let val_for_guard = self.storage_live_binding(
-                        block, binding.var_id, binding.span, ValWithinGuard);
-                    self.schedule_drop_for_binding(binding.var_id, binding.span, ValWithinGuard);
+                        block, binding.var_id, binding.span, ValWithinGuard(pat_index));
+                    self.schedule_drop_for_binding(
+                        binding.var_id, binding.span, ValWithinGuard(pat_index));
 
                     // rust-lang/rust#27282: We reuse the two-phase
                     // borrow infrastructure so that the mutable
@@ -1146,16 +1158,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
     /// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where
     /// the bound `var` has type `T` in the arm body) in a pattern
-    /// maps to *two* locals. The first local is a binding for
+    /// maps to `2+N` locals. The first local is a binding for
     /// occurrences of `var` in the guard, which will all have type
-    /// `&T`. The second local is a binding for occurrences of `var`
-    /// in the arm body, which will have type `T`.
+    /// `&T`. The N locals are bindings for the `T` that is referenced
+    /// by the first local; they are not used outside of the
+    /// guard. The last local is a binding for occurrences of `var` in
+    /// the arm body, which will have type `T`.
+    ///
+    /// The reason we have N locals rather than just 1 is to
+    /// accommodate rust-lang/rust#51348: If the arm has N candidate
+    /// patterns, then in general they can correspond to distinct
+    /// parts of the matched data, and we want them to be distinct
+    /// temps in order to simplify checks performed by our internal
+    /// leveraging of two-phase borrows).
     fn declare_binding(&mut self,
                        source_info: SourceInfo,
                        visibility_scope: SourceScope,
                        mutability: Mutability,
                        name: Name,
                        mode: BindingMode,
+                       num_patterns: usize,
                        var_id: NodeId,
                        var_ty: Ty<'tcx>,
                        has_guard: ArmHasGuard,
@@ -1189,7 +1211,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         };
         let for_arm_body = self.local_decls.push(local.clone());
         let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() {
-            let val_for_guard =  self.local_decls.push(local);
+            let mut vals_for_guard = Vec::with_capacity(num_patterns);
+            for _ in 0..num_patterns {
+                let val_for_guard_idx =  self.local_decls.push(local.clone());
+                vals_for_guard.push(val_for_guard_idx);
+            }
             let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
                 mutability,
                 ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty),
@@ -1200,7 +1226,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 internal: false,
                 is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)),
             });
-            LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body }
+            LocalsForNode::ForGuard { vals_for_guard, ref_for_guard, for_arm_body }
         } else {
             LocalsForNode::One(for_arm_body)
         };
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index e2b460f69fd..52eafad43b3 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -631,6 +631,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             bindings: candidate.bindings.clone(),
             guard: candidate.guard.clone(),
             arm_index: candidate.arm_index,
+            pat_index: candidate.pat_index,
             pre_binding_block: candidate.pre_binding_block,
             next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
         }
@@ -694,6 +695,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             bindings: candidate.bindings.clone(),
             guard: candidate.guard.clone(),
             arm_index: candidate.arm_index,
+            pat_index: candidate.pat_index,
             pre_binding_block: candidate.pre_binding_block,
             next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
         }
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 24228389fbf..054bd69c361 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -310,8 +310,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
 #[derive(Debug)]
 enum LocalsForNode {
+    /// In the usual case, a node-id for an identifier maps to at most
+    /// one Local declaration.
     One(Local),
-    Three { val_for_guard: Local, ref_for_guard: Local, for_arm_body: Local },
+
+    /// The exceptional case is identifiers in a match arm's pattern
+    /// that are referenced in a guard of that match arm. For these,
+    /// we can have `2+k` Locals, where `k` is the number of candidate
+    /// patterns (separated by `|`) in the arm.
+    ///
+    /// * `for_arm_body` is the Local used in the arm body (which is
+    ///   just like the `One` case above),
+    ///
+    /// * `ref_for_guard` is the Local used in the arm's guard (which
+    ///   is a reference to a temp that is an alias of
+    ///   `for_arm_body`).
+    ///
+    /// * `vals_for_guard` is the `k` Locals; at most one of them will
+    ///   get initialized by the arm's execution, and after it is
+    ///   initialized, `ref_for_guard` will be assigned a reference to
+    ///   it.
+    ///
+    /// There reason we have `k` Locals rather than just 1 is to
+    /// accommodate some restrictions imposed by two-phase borrows,
+    /// which apply when we have a `ref mut` pattern.
+    ForGuard { vals_for_guard: Vec<Local>, ref_for_guard: Local, for_arm_body: Local },
 }
 
 #[derive(Debug)]
@@ -350,7 +373,10 @@ struct GuardFrame {
 ///   3. the temp for use outside of guard expressions.
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum ForGuard {
-    ValWithinGuard,
+    /// The `usize` identifies for which candidate pattern we want the
+    /// local binding. We keep a temp per-candidate to accommodate
+    /// two-phase borrows (see `LocalsForNode` documentation).
+    ValWithinGuard(usize),
     RefWithinGuard,
     OutsideGuard,
 }
@@ -359,12 +385,15 @@ impl LocalsForNode {
     fn local_id(&self, for_guard: ForGuard) -> Local {
         match (self, for_guard) {
             (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) |
-            (&LocalsForNode::Three { val_for_guard: local_id, .. }, ForGuard::ValWithinGuard) |
-            (&LocalsForNode::Three { ref_for_guard: local_id, .. }, ForGuard::RefWithinGuard) |
-            (&LocalsForNode::Three { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) =>
+            (&LocalsForNode::ForGuard { ref_for_guard: local_id, .. }, ForGuard::RefWithinGuard) |
+            (&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) =>
                 local_id,
 
-            (&LocalsForNode::One(_), ForGuard::ValWithinGuard) |
+            (&LocalsForNode::ForGuard { ref vals_for_guard, .. },
+             ForGuard::ValWithinGuard(pat_idx)) =>
+                vals_for_guard[pat_idx],
+
+            (&LocalsForNode::One(_), ForGuard::ValWithinGuard(_)) |
             (&LocalsForNode::One(_), ForGuard::RefWithinGuard) =>
                 bug!("anything with one local should never be within a guard."),
         }
@@ -740,7 +769,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     }
                     _ => {
                         scope = self.declare_bindings(scope, ast_body.span,
-                                                      LintLevel::Inherited, &pattern,
+                                                      LintLevel::Inherited, &[pattern.clone()],
                                                       matches::ArmHasGuard(false),
                                                       Some((Some(&place), span)));
                         unpack!(block = self.place_into_pattern(block, pattern, &place, false));