]> git.lizzy.rs Git - rust.git/commitdiff
Implement general or-patterns in `let` statements
authorMatthew Jasper <mjjasper1@gmail.com>
Fri, 27 Dec 2019 17:31:21 +0000 (17:31 +0000)
committerMatthew Jasper <mjjasper1@gmail.com>
Sat, 1 Feb 2020 22:10:43 +0000 (22:10 +0000)
src/librustc_mir_build/build/matches/mod.rs

index 4ac3ced17dbb1b08e04fd46d40e042cd47a65dbc..928363246c2c0caf710c79a1d101a647a0c158d8 100644 (file)
@@ -96,11 +96,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let mut arm_candidates = self.create_match_candidates(&scrutinee_place, &arms);
 
         let match_has_guard = arms.iter().any(|arm| arm.guard.is_some());
-        let candidates =
+        let mut candidates =
             arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
 
         let fake_borrow_temps =
-            self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates);
+            self.lower_match_tree(block, scrutinee_span, match_has_guard, &mut candidates);
 
         self.lower_match_arms(
             &destination,
@@ -181,7 +181,7 @@ fn lower_match_tree<'pat>(
         block: BasicBlock,
         scrutinee_span: Span,
         match_has_guard: bool,
-        mut candidates: Vec<&mut Candidate<'pat, 'tcx>>,
+        candidates: &mut [&mut Candidate<'pat, 'tcx>],
     ) -> Vec<(Place<'tcx>, Local)> {
         // The set of places that we are creating fake borrows of. If there are
         // no match guards then we don't need any fake borrows, so don't track
@@ -192,13 +192,7 @@ fn lower_match_tree<'pat>(
 
         // This will generate code to test scrutinee_place and
         // branch to the appropriate arm block
-        self.match_candidates(
-            scrutinee_span,
-            block,
-            &mut otherwise,
-            &mut candidates,
-            &mut fake_borrows,
-        );
+        self.match_candidates(scrutinee_span, block, &mut otherwise, candidates, &mut fake_borrows);
 
         if let Some(otherwise_block) = otherwise {
             let source_info = self.source_info(scrutinee_span);
@@ -207,7 +201,7 @@ fn lower_match_tree<'pat>(
 
         let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
 
-        for candidate in candidates.into_iter() {
+        for candidate in candidates {
             candidate.visit_leaves(|leaf_candidate| {
                 if let Some(ref mut prev) = previous_candidate {
                     prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block;
@@ -263,7 +257,7 @@ fn lower_match_arms(
                         arm.guard.as_ref().map(|g| (g, match_scope)),
                         &fake_borrow_temps,
                         scrutinee_span,
-                        arm.scope,
+                        Some(arm.scope),
                     );
 
                     if let Some(source_scope) = scope {
@@ -297,7 +291,7 @@ fn bind_pattern(
         guard: Option<(&Guard<'tcx>, region::Scope)>,
         fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
         scrutinee_span: Span,
-        arm_scope: region::Scope,
+        arm_scope: Option<region::Scope>,
     ) -> BasicBlock {
         if candidate.subcandidates.is_empty() {
             // Avoid generating another `BasicBlock` when we only have one
@@ -308,10 +302,11 @@ fn bind_pattern(
                 guard,
                 fake_borrow_temps,
                 scrutinee_span,
+                true,
             )
         } else {
             let target_block = self.cfg.start_new_block();
-
+            let mut schedule_drops = true;
             // We keep a stack of all of the bindings and type asciptions
             // from the the parent candidates that we visit, that also need to
             // be bound for each candidate.
@@ -319,14 +314,24 @@ fn bind_pattern(
                 candidate,
                 &mut Vec::new(),
                 &mut |leaf_candidate, parent_bindings| {
-                    self.clear_top_scope(arm_scope);
+                    if let Some(arm_scope) = arm_scope {
+                        // Avoid scheduling drops multiple times by unscheduling drops.
+                        self.clear_top_scope(arm_scope);
+                    }
                     let binding_end = self.bind_and_guard_matched_candidate(
                         leaf_candidate,
                         parent_bindings,
                         guard,
                         &fake_borrow_temps,
                         scrutinee_span,
+                        schedule_drops,
                     );
+                    if arm_scope.is_none() {
+                        // If we aren't in a match, then our bindings may not be
+                        // the only thing in the top scope, so only schedule
+                        // them to drop for the first pattern instead.
+                        schedule_drops = false;
+                    }
                     self.cfg.goto(binding_end, outer_source_info, target_block);
                 },
                 |inner_candidate, parent_bindings| {
@@ -460,51 +465,43 @@ pub(super) fn expr_into_pattern(
             subcandidates: vec![],
         };
 
-        // Simplify the candidate. Since the pattern is irrefutable, this should
-        // always convert all match-pairs into bindings.
-        self.simplify_candidate(&mut candidate);
-
-        if !candidate.match_pairs.is_empty() {
-            // ICE if no other errors have been emitted. This used to be a hard error that wouldn't
-            // be reached because `hair::pattern::check_match::check_match` wouldn't have let the
-            // compiler continue. In our tests this is only ever hit by
-            // `ui/consts/const-match-check.rs` with `--cfg eval1`, and that file already generates
-            // a different error before hand.
-            self.hir.tcx().sess.delay_span_bug(
-                candidate.match_pairs[0].pattern.span,
-                &format!(
-                    "match pairs {:?} remaining after simplifying irrefutable pattern",
-                    candidate.match_pairs,
-                ),
-            );
-        }
+        let fake_borrow_temps =
+            self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]);
 
         // for matches and function arguments, the place that is being matched
         // can be set when creating the variables. But the place for
         // let PATTERN = ... might not even exist until we do the assignment.
         // so we set it here instead
         if set_match_place {
-            for binding in &candidate.bindings {
-                let local = self.var_local_id(binding.var_id, OutsideGuard);
-
-                if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
-                    opt_match_place: Some((ref mut match_place, _)),
-                    ..
-                }))) = self.local_decls[local].local_info
-                {
-                    *match_place = Some(*initializer);
-                } else {
-                    bug!("Let binding to non-user variable.")
+            let mut candidate_ref = &candidate;
+            while let Some(next) = {
+                for binding in &candidate_ref.bindings {
+                    let local = self.var_local_id(binding.var_id, OutsideGuard);
+
+                    if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+                        VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
+                    ))) = self.local_decls[local].local_info
+                    {
+                        *match_place = Some(*initializer);
+                    } else {
+                        bug!("Let binding to non-user variable.")
+                    }
                 }
+                candidate_ref.subcandidates.get(0)
+            } {
+                candidate_ref = next;
             }
         }
 
-        self.ascribe_types(block, &candidate.ascriptions);
-
-        // now apply the bindings, which will also declare the variables
-        self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
-
-        block.unit()
+        self.bind_pattern(
+            self.source_info(irrefutable_pat.span),
+            candidate,
+            None,
+            &fake_borrow_temps,
+            irrefutable_pat.span,
+            None,
+        )
+        .unit()
     }
 
     /// Declares the bindings of the given patterns and returns the visibility
@@ -1486,6 +1483,7 @@ fn bind_and_guard_matched_candidate<'pat>(
         guard: Option<(&Guard<'tcx>, region::Scope)>,
         fake_borrows: &Vec<(Place<'tcx>, Local)>,
         scrutinee_span: Span,
+        schedule_drops: bool,
     ) -> BasicBlock {
         debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
 
@@ -1692,7 +1690,8 @@ fn bind_and_guard_matched_candidate<'pat>(
                 let cause = FakeReadCause::ForGuardBinding;
                 self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
             }
-            self.bind_matched_candidate_for_arm_body(post_guard_block, by_value_bindings);
+            assert!(schedule_drops, "patterns with guards must schedule drops");
+            self.bind_matched_candidate_for_arm_body(post_guard_block, true, by_value_bindings);
 
             post_guard_block
         } else {
@@ -1701,6 +1700,7 @@ fn bind_and_guard_matched_candidate<'pat>(
             // that we have to inspect before we bind them.)
             self.bind_matched_candidate_for_arm_body(
                 block,
+                schedule_drops,
                 parent_bindings
                     .iter()
                     .flat_map(|(bindings, _)| bindings)
@@ -1793,6 +1793,7 @@ fn bind_matched_candidate_for_guard<'b>(
     fn bind_matched_candidate_for_arm_body<'b>(
         &mut self,
         block: BasicBlock,
+        schedule_drops: bool,
         bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
     ) where
         'tcx: 'b,
@@ -1805,7 +1806,9 @@ fn bind_matched_candidate_for_arm_body<'b>(
             let source_info = self.source_info(binding.span);
             let local =
                 self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard);
-            self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
+            if schedule_drops {
+                self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
+            }
             let rvalue = match binding.binding_mode {
                 BindingMode::ByValue => {
                     Rvalue::Use(self.consume_by_copy_or_move(binding.source.clone()))