]> git.lizzy.rs Git - rust.git/commitdiff
Give match arms a drop/region scope
authorMatthew Jasper <mjjasper1@gmail.com>
Wed, 3 Apr 2019 18:21:51 +0000 (19:21 +0100)
committerMatthew Jasper <mjjasper1@gmail.com>
Tue, 21 May 2019 18:37:38 +0000 (19:37 +0100)
Also give arms the correct lint scope in MIR.

src/librustc/cfg/construct.rs
src/librustc/middle/region.rs
src/librustc_mir/build/matches/mod.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/hair/cx/expr.rs
src/test/mir-opt/match_false_edges.rs
src/test/mir-opt/match_test.rs
src/test/mir-opt/remove_fake_borrows.rs
src/test/ui/lint/lint-unused-mut-variables.rs
src/test/ui/lint/lint-unused-mut-variables.stderr

index 2e54165be1f1baf485b42ac331c8922ae7694b2e..ef0d4be268eaf36d1c60f2169f9f9f4bec3f1542 100644 (file)
@@ -419,7 +419,7 @@ fn match_(&mut self, id: hir::ItemLocalId, discr: &hir::Expr,
         for arm in arms {
             // Add an exit node for when we've visited all the
             // patterns and the guard (if there is one) in the arm.
-            let arm_exit = self.add_dummy_node(&[]);
+            let bindings_exit = self.add_dummy_node(&[]);
 
             for pat in &arm.pats {
                 // Visit the pattern, coming from the discriminant exit
@@ -453,14 +453,16 @@ fn match_(&mut self, id: hir::ItemLocalId, discr: &hir::Expr,
 
                 // Add an edge from the exit of this pattern to the
                 // exit of the arm
-                self.add_contained_edge(pat_exit, arm_exit);
+                self.add_contained_edge(pat_exit, bindings_exit);
             }
 
             // Visit the body of this arm
-            let body_exit = self.expr(&arm.body, arm_exit);
+            let body_exit = self.expr(&arm.body, bindings_exit);
+
+            let arm_exit = self.add_ast_node(arm.hir_id.local_id, &[body_exit]);
 
             // Link the body to the exit of the expression
-            self.add_contained_edge(body_exit, expr_exit);
+            self.add_contained_edge(arm_exit, expr_exit);
         }
 
         expr_exit
index 37681ad7fcdd2bc51708264a8246e7a8a3ede9f0..fa4e8e3d4769d34e2645048a60402aac2ff1757b 100644 (file)
@@ -119,18 +119,18 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 pub enum ScopeData {
     Node,
 
-    // Scope of the call-site for a function or closure
-    // (outlives the arguments as well as the body).
+    /// Scope of the call-site for a function or closure
+    /// (outlives the arguments as well as the body).
     CallSite,
 
-    // Scope of arguments passed to a function or closure
-    // (they outlive its body).
+    /// Scope of arguments passed to a function or closure
+    /// (they outlive its body).
     Arguments,
 
-    // Scope of destructors for temporaries of node-id.
+    /// Scope of destructors for temporaries of node-id.
     Destruction,
 
-    // Scope following a `let id = expr;` binding in a block.
+    /// Scope following a `let id = expr;` binding in a block.
     Remainder(FirstStatementIndex)
 }
 
@@ -152,11 +152,11 @@ pub enum ScopeData {
     ///
     /// * The subscope with `first_statement_index == 1` is scope of `c`,
     ///   and thus does not include EXPR_2, but covers the `...`.
-    pub struct FirstStatementIndex { .. }
+    pub struct FirstStatementIndex {
+        derive [HashStable]
+    }
 }
 
-impl_stable_hash_for!(struct crate::middle::region::FirstStatementIndex { private });
-
 // compilation error if size of `ScopeData` is not the same as a `u32`
 static_assert_size!(ScopeData, 4);
 
@@ -814,6 +814,16 @@ fn resolve_block<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, blk:
 }
 
 fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &'tcx hir::Arm) {
+    let prev_cx = visitor.cx;
+
+    visitor.enter_scope(
+        Scope {
+            id: arm.hir_id.local_id,
+            data: ScopeData::Node,
+        }
+    );
+    visitor.cx.var_parent = visitor.cx.parent;
+
     visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
 
     if let Some(hir::Guard::If(ref expr)) = arm.guard {
@@ -821,6 +831,8 @@ fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &
     }
 
     intravisit::walk_arm(visitor, arm);
+
+    visitor.cx = prev_cx;
 }
 
 fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &'tcx hir::Pat) {
@@ -893,10 +905,6 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
                 terminating(body.hir_id.local_id);
             }
 
-            hir::ExprKind::Match(..) => {
-                visitor.cx.var_parent = visitor.cx.parent;
-            }
-
             hir::ExprKind::DropTemps(ref expr) => {
                 // `DropTemps(expr)` does not denote a conditional scope.
                 // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.
index 93a702dc44e25254f7f90387ad9a7fde57ea13a2..091e39630d6cb87f9969b9a8d7d3b352b942f607 100644 (file)
@@ -12,6 +12,7 @@
 use crate::hair::{self, *};
 use rustc::hir::HirId;
 use rustc::mir::*;
+use rustc::middle::region;
 use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
 use rustc::ty::layout::VariantIdx;
 use rustc_data_structures::bit_set::BitSet;
@@ -251,37 +252,39 @@ pub fn match_expr(
 
         // Step 5. Create everything else: the guards and the arms.
 
-        let outer_source_info = self.source_info(span);
         let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, candidates)| {
-            let mut arm_block = self.cfg.start_new_block();
-
-            let body = self.hir.mirror(arm.body.clone());
-            let scope = self.declare_bindings(
-                None,
-                body.span,
-                &arm.patterns[0],
-                ArmHasGuard(arm.guard.is_some()),
-                Some((Some(&scrutinee_place), scrutinee_span)),
-            );
-
-            if let Some(source_scope) = scope {
-                this.source_scope = source_scope;
-            }
-
-            for candidate in candidates {
-                self.bind_and_guard_matched_candidate(
-                    candidate,
-                    arm.guard.clone(),
-                    arm_block,
-                    &fake_borrow_temps,
-                    scrutinee_span,
+            let arm_source_info = self.source_info(arm.span);
+            let region_scope = (arm.scope, arm_source_info);
+            self.in_scope(region_scope, arm.lint_level, |this| {
+                let arm_block = this.cfg.start_new_block();
+
+                let body = this.hir.mirror(arm.body.clone());
+                let scope = this.declare_bindings(
+                    None,
+                    arm.span,
+                    &arm.patterns[0],
+                    ArmHasGuard(arm.guard.is_some()),
+                    Some((Some(&scrutinee_place), scrutinee_span)),
                 );
-            }
 
+                if let Some(source_scope) = scope {
+                    this.source_scope = source_scope;
+                }
 
-            unpack!(arm_block = self.into(destination, arm_block, body));
+                for candidate in candidates {
+                    this.clear_top_scope(arm.scope);
+                    this.bind_and_guard_matched_candidate(
+                        candidate,
+                        arm.guard.clone(),
+                        arm_block,
+                        &fake_borrow_temps,
+                        scrutinee_span,
+                        region_scope,
+                    );
+                }
 
-            arm_block
+                this.into(destination, arm_block, body)
+            })
         }).collect();
 
         // all the arm blocks will rejoin here
@@ -289,7 +292,7 @@ pub fn match_expr(
 
         for arm_block in arm_end_blocks {
             self.cfg.terminate(
-                arm_block,
+                unpack!(arm_block),
                 outer_source_info,
                 TerminatorKind::Goto { target: end_block },
             );
@@ -502,7 +505,7 @@ pub fn declare_bindings(
                     visibility_scope =
                         Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
                 }
-                let source_info = SourceInfo { span, this.source_scope };
+                let source_info = SourceInfo { span, scope: this.source_scope };
                 let visibility_scope = visibility_scope.unwrap();
                 this.declare_binding(
                     source_info,
@@ -1315,6 +1318,7 @@ fn bind_and_guard_matched_candidate<'pat>(
         arm_block: BasicBlock,
         fake_borrows: &Vec<(&Place<'tcx>, Local)>,
         scrutinee_span: Span,
+        region_scope: (region::Scope, SourceInfo),
     ) {
         debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
 
@@ -1497,17 +1501,40 @@ fn bind_and_guard_matched_candidate<'pat>(
             //
             // and that is clearly not correct.
             let post_guard_block = self.cfg.start_new_block();
+            let otherwise_post_guard_block = self.cfg.start_new_block();
             self.cfg.terminate(
                 block,
                 source_info,
                 TerminatorKind::if_(
                     self.hir.tcx(),
-                    cond,
+                    cond.clone(),
                     post_guard_block,
-                    candidate.otherwise_block.unwrap()
+                    otherwise_post_guard_block,
                 ),
             );
 
+            self.exit_scope(
+                source_info.span,
+                region_scope,
+                otherwise_post_guard_block,
+                candidate.otherwise_block.unwrap(),
+            );
+
+            if let Operand::Copy(cond_place) | Operand::Move(cond_place) = cond {
+                if let Place::Base(PlaceBase::Local(cond_temp)) = cond_place {
+                    // We will call `clear_top_scope` if there's another guard. So
+                    // we have to drop this variable now or it will be "storage
+                    // leaked".
+                    self.pop_variable(
+                        post_guard_block,
+                        region_scope.0,
+                        cond_temp
+                    );
+                } else {
+                    bug!("Expected as_local_operand to produce a temporary");
+                }
+            }
+
             let by_value_bindings = candidate.bindings.iter().filter(|binding| {
                 if let BindingMode::ByValue = binding.binding_mode { true } else { false }
             });
index 471304012c9a803bf54a5f0446be9e409d45a9ee..0d1d40a8af6333b42d3f9c73cebacf8c88e8ac7d 100644 (file)
 them. Eventually, when we shift to non-lexical lifetimes, there should
 be no need to remember this mapping.
 
-There is one additional wrinkle, actually, that I wanted to hide from
-you but duty compels me to mention. In the course of building
-matches, it sometimes happen that certain code (namely guards) gets
-executed multiple times. This means that the scope lexical scope may
-in fact correspond to multiple, disjoint SEME regions. So in fact our
+### Not so SEME Regions
+
+In the course of building matches, it sometimes happens that certain code
+(namely guards) gets executed multiple times. This means that the scope lexical
+scope may in fact correspond to multiple, disjoint SEME regions. So in fact our
 mapping is from one scope to a vector of SEME regions.
 
+Also in matches, the scopes assigned to arms are not even SEME regions! Each
+arm has a single region with one entry for each pattern. We manually
+manipulate the scheduled drops in this scope to avoid dropping things multiple
+times, although drop elaboration would clean this up for value drops.
+
 ### Drops
 
 The primary purpose for scopes is to insert drops: while building
@@ -731,7 +736,7 @@ pub fn schedule_drop(
             // Note that this code iterates scopes from the inner-most to the outer-most,
             // invalidating caches of each scope visited. This way bare minimum of the
             // caches gets invalidated. i.e., if a new drop is added into the middle scope, the
-            // cache of outer scpoe stays intact.
+            // cache of outer scope stays intact.
             scope.invalidate_cache(!needs_drop, this_scope);
             if this_scope {
                 if let DropKind::Value { .. } = drop_kind {
@@ -873,6 +878,73 @@ pub fn assert(&mut self, block: BasicBlock,
 
         success_block
     }
+
+    // `match` arm scopes
+    // ==================
+    /// Unschedules any drops in the top scope.
+    ///
+    /// This is only needed for `match` arm scopes, because they have one
+    /// entrance per pattern, but only one exit.
+    pub fn clear_top_scope(&mut self, region_scope: region::Scope) {
+        let top_scope = self.scopes.last_mut().unwrap();
+
+        assert_eq!(top_scope.region_scope, region_scope);
+
+        top_scope.drops.clear();
+        top_scope.invalidate_cache(false, true);
+    }
+
+    /// Drops the single variable provided
+    ///
+    /// * The scope must be the top scope.
+    /// * The variable must be in that scope.
+    /// * The variable must be at the top of that scope: it's the next thing
+    ///   scheduled to drop.
+    /// * The drop must be of DropKind::Storage.
+    ///
+    /// This is used for the boolean holding the result of the match guard. We
+    /// do this because:
+    ///
+    /// * The boolean is different for each pattern
+    /// * There is only one exit for the arm scope
+    /// * The guard expression scope is too short, it ends just before the
+    ///   boolean is tested.
+    pub fn pop_variable(
+        &mut self,
+        block: BasicBlock,
+        region_scope: region::Scope,
+        variable: Local,
+    ) {
+        let top_scope = self.scopes.last_mut().unwrap();
+
+        assert_eq!(top_scope.region_scope, region_scope);
+
+        let top_drop_data = top_scope.drops.pop().unwrap();
+
+        match top_drop_data.kind {
+            DropKind::Value { .. } => {
+                bug!("Should not be calling pop_top_variable on non-copy type!")
+            }
+            DropKind::Storage => {
+                // Drop the storage for both value and storage drops.
+                // Only temps and vars need their storage dead.
+                match top_drop_data.location {
+                    Place::Base(PlaceBase::Local(index)) => {
+                        let source_info = top_scope.source_info(top_drop_data.span);
+                        assert_eq!(index, variable);
+                        self.cfg.push(block, Statement {
+                            source_info,
+                            kind: StatementKind::StorageDead(index)
+                        });
+                    }
+                    _ => unreachable!(),
+                }
+            }
+        }
+
+        top_scope.invalidate_cache(true, true);
+    }
+
 }
 
 /// Builds drops for pop_scope and exit_scope.
index 50140880a368df329bdbf08b35f3cb833cffb9a4..d623f149988c7aed7893e9d1cc9614f7d26c9013 100644 (file)
@@ -879,8 +879,12 @@ fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm)
                 _ => None,
             },
         body: arm.body.to_ref(),
-        // BUG: fix this
-        lint_level: LintLevel::Inherited,
+        lint_level: LintLevel::Explicit(arm.hir_id),
+        scope: region::Scope {
+            id: arm.hir_id.local_id,
+            data: region::ScopeData::Node
+        },
+        span: arm.span,
     }
 }
 
index 7ac36a22274f33897dfe8e2a827b7c97f373a010..0850c552536c505d233444e2e679c7ae454d70d7 100644 (file)
@@ -45,13 +45,13 @@ fn main() {
 //      _2 = std::option::Option::<i32>::Some(const 42i32,);
 //      FakeRead(ForMatchedPlace, _2);
 //      _3 = discriminant(_2);
-//      switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb7];
+//      switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb6];
 //  }
 //  bb1 (cleanup): {
 //      resume;
 //  }
 //  bb2: {
-//      falseEdges -> [real: bb8, imaginary: bb3]; //pre_binding1
+//      falseEdges -> [real: bb7, imaginary: bb3]; //pre_binding1
 //  }
 //  bb3: {
 //      falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2
@@ -62,48 +62,56 @@ fn main() {
 //  bb5: {
 //      unreachable;
 //  }
-//  bb6: { // to pre_binding2
-//      falseEdges -> [real: bb3, imaginary: bb3];
-//  }
-//  bb7: {
+//  bb6: {
 //      unreachable;
 //  }
-//  bb8: { // binding1 and guard
+//  bb7: { // binding1 and guard
 //      StorageLive(_6);
 //      _6 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
 //      _4 = &shallow _2;
 //      StorageLive(_7);
-//      _7 = const guard() -> [return: bb9, unwind: bb1];
+//      _7 = const guard() -> [return: bb8, unwind: bb1];
 //  }
-//  bb9: {
+//  bb8: { // end of guard
 //      FakeRead(ForMatchGuard, _4);
 //      FakeRead(ForGuardBinding, _6);
-//      switchInt(move _7) -> [false: bb6, otherwise: bb10];
+//      switchInt(move _7) -> [false: bb10, otherwise: bb9];
 //  }
-//  bb10: {
+//  bb9: { // arm1
+//      StorageDead(_7);
 //      StorageLive(_5);
 //      _5 = ((_2 as Some).0: i32);
 //      StorageLive(_8);
 //      _8 = _5;
 //      _1 = (const 1i32, move _8);
 //      StorageDead(_8);
+//      StorageDead(_5);
+//      StorageDead(_6);
 //      goto -> bb13;
 //  }
-//  bb11: {
+//  bb10: { // to pre_binding2
+//      StorageDead(_7);
+//      StorageDead(_6);
+//      falseEdges -> [real: bb3, imaginary: bb3];
+//  }
+//  bb11: { // arm2
 //      StorageLive(_9);
 //      _9 = ((_2 as Some).0: i32);
 //      StorageLive(_10);
 //      _10 = _9;
 //      _1 = (const 2i32, move _10);
 //      StorageDead(_10);
+//      StorageDead(_9);
 //      goto -> bb13;
 //  }
-//  bb12: {
+//  bb12: { // arm3
 //      _1 = (const 3i32, const 3i32);
 //      goto -> bb13;
 //  }
 //  bb13: {
-//      ...
+//      StorageDead(_1);
+//      StorageDead(_2);
+//      _0 = ();
 //      return;
 //  }
 // END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir
@@ -114,13 +122,13 @@ fn main() {
 //      _2 = std::option::Option::<i32>::Some(const 42i32,);
 //      FakeRead(ForMatchedPlace, _2);
 //      _3 = discriminant(_2);
-//      switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb7];
+//      switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb6];
 //  }
 //  bb1 (cleanup): {
 //      resume;
 //  }
 //  bb2: {
-//      falseEdges -> [real: bb8, imaginary: bb3];
+//      falseEdges -> [real: bb7, imaginary: bb3];
 //  }
 //  bb3: {
 //      falseEdges -> [real: bb11, imaginary: bb4];
@@ -131,33 +139,38 @@ fn main() {
 //  bb5: {
 //      unreachable;
 //  }
-//  bb6: { // to pre_binding3 (can skip 2 since this is `Some`)
-//      falseEdges -> [real: bb4, imaginary: bb3];
-//  }
-//  bb7: {
+//  bb6: {
 //      unreachable;
 //  }
-//  bb8: { // binding1 and guard
+//  bb7: { // binding1 and guard
 //      StorageLive(_6);
 //      _6 = &((_2 as Some).0: i32);
 //      _4 = &shallow _2;
 //      StorageLive(_7);
-//      _7 = const guard() -> [return: bb9, unwind: bb1];
+//      _7 = const guard() -> [return: bb8, unwind: bb1];
 //  }
-//  bb9: { // end of guard
+//  bb8: { // end of guard
 //      FakeRead(ForMatchGuard, _4);
 //      FakeRead(ForGuardBinding, _6);
-//      switchInt(move _7) -> [false: bb6, otherwise: bb10];
+//      switchInt(move _7) -> [false: bb10, otherwise: bb9];
 //  }
-//  bb10: { // arm1
+//  bb9: { // arm1
+//      StorageDead(_7);
 //      StorageLive(_5);
 //      _5 = ((_2 as Some).0: i32);
 //      StorageLive(_8);
 //      _8 = _5;
 //      _1 = (const 1i32, move _8);
 //      StorageDead(_8);
+//      StorageDead(_5);
+//      StorageDead(_6);
 //      goto -> bb13;
 //  }
+//  bb10: { // to pre_binding3 (can skip 2 since this is `Some`)
+//      StorageDead(_7);
+//      StorageDead(_6);
+//      falseEdges -> [real: bb4, imaginary: bb3];
+//  }
 //  bb11: { // arm2
 //      _1 = (const 3i32, const 3i32);
 //      goto -> bb13;
@@ -169,16 +182,19 @@ fn main() {
 //      _10 = _9;
 //      _1 = (const 2i32, move _10);
 //      StorageDead(_10);
+//      StorageDead(_9);
 //      goto -> bb13;
 //  }
 //  bb13: {
-//      ...
+//      StorageDead(_1);
+//      StorageDead(_2);
+//      _0 = ();
 //      return;
 //  }
 // END rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir
 //
 // START rustc.main.QualifyAndPromoteConstants.before.mir
-// bb0: {
+//  bb0: {
 //     ...
 //      _2 = std::option::Option::<i32>::Some(const 1i32,);
 //      FakeRead(ForMatchedPlace, _2);
@@ -189,13 +205,13 @@ fn main() {
 //      resume;
 //  }
 //  bb2: {
-//      falseEdges -> [real: bb9, imaginary: bb3];
+//      falseEdges -> [real: bb7, imaginary: bb3];
 //  }
 //  bb3: {
-//      falseEdges -> [real: bb12, imaginary: bb4];
+//      falseEdges -> [real: bb11, imaginary: bb4];
 //  }
 //  bb4: {
-//      falseEdges -> [real: bb13, imaginary: bb5];
+//      falseEdges -> [real: bb12, imaginary: bb5];
 //  }
 //  bb5: {
 //      falseEdges -> [real: bb16, imaginary: bb6];
@@ -203,65 +219,79 @@ fn main() {
 //  bb6: {
 //      unreachable;
 //  }
-//  bb7: {
-//      falseEdges -> [real: bb3, imaginary: bb3];
-//  }
-//  bb8: {
-//      falseEdges -> [real: bb5, imaginary: bb5];
-//  }
-//  bb9: { // binding1: Some(w) if guard()
+//  bb7: { // binding1: Some(w) if guard()
 //      StorageLive(_7);
 //      _7 = &((_2 as Some).0: i32);
 //      _5 = &shallow _2;
 //      StorageLive(_8);
-//      _8 = const guard() -> [return: bb10, unwind: bb1];
+//      _8 = const guard() -> [return: bb8, unwind: bb1];
 //  }
-//  bb10: { //end of guard
+//  bb8: { //end of guard1
 //      FakeRead(ForMatchGuard, _5);
 //      FakeRead(ForGuardBinding, _7);
-//      switchInt(move _8) -> [false: bb7, otherwise: bb11];
+//      switchInt(move _8) -> [false: bb10, otherwise: bb9];
 //  }
-//  bb11: { // set up bindings for arm1
+//  bb9: {
+//      StorageDead(_8);
 //      StorageLive(_6);
 //      _6 = ((_2 as Some).0: i32);
 //      _1 = const 1i32;
+//      StorageDead(_6);
+//      StorageDead(_7);
 //      goto -> bb17;
 //  }
-//  bb12: { // binding2 & arm2
+//  bb10: {
+//      StorageDead(_8);
+//      StorageDead(_7);
+//      falseEdges -> [real: bb3, imaginary: bb3];
+//  }
+//  bb11: { // binding2 & arm2
 //      StorageLive(_9);
 //      _9 = _2;
 //      _1 = const 2i32;
+//      StorageDead(_9);
 //      goto -> bb17;
 //  }
-//  bb13: { // binding3: Some(y) if guard2(y)
+//  bb12: { // binding3: Some(y) if guard2(y)
 //      StorageLive(_11);
 //      _11 = &((_2 as Some).0: i32);
 //      _5 = &shallow _2;
 //      StorageLive(_12);
 //      StorageLive(_13);
 //      _13 = (*_11);
-//      _12 = const guard2(move _13) -> [return: bb14, unwind: bb1];
+//      _12 = const guard2(move _13) -> [return: bb13, unwind: bb1];
 //  }
-//  bb14: { // end of guard2
+//  bb13: { // end of guard2
 //      StorageDead(_13);
 //      FakeRead(ForMatchGuard, _5);
 //      FakeRead(ForGuardBinding, _11);
-//      switchInt(move _12) -> [false: bb8, otherwise: bb15];
+//      switchInt(move _12) -> [false: bb15, otherwise: bb14];
 //  }
-//  bb15: { // binding4 & arm4
+//  bb14: { // binding4 & arm4
+//      StorageDead(_12);
 //      StorageLive(_10);
 //      _10 = ((_2 as Some).0: i32);
 //      _1 = const 3i32;
+//      StorageDead(_10);
+//      StorageDead(_11);
 //      goto -> bb17;
 //  }
+//  bb15: {
+//      StorageDead(_12);
+//      StorageDead(_11);
+//      falseEdges -> [real: bb5, imaginary: bb5];
+//  }
 //  bb16: {
 //      StorageLive(_14);
 //      _14 = _2;
 //      _1 = const 4i32;
+//      StorageDead(_14);
 //      goto -> bb17;
 //  }
 //  bb17: {
-//      ...
+//      StorageDead(_1);
+//      StorageDead(_2);
+//      _0 = ();
 //      return;
 //  }
 // END rustc.main.QualifyAndPromoteConstants.before.mir
index a5317f98ef1885df9f20df65aae3a88a0172671d..2ef9520c12c637e54c6bb00191c85d01f950a583 100644 (file)
@@ -20,10 +20,10 @@ fn main() {
 // START rustc.main.SimplifyCfg-initial.after.mir
 //    bb0: {
 //        ...
-//        switchInt(move _4) -> [false: bb7, otherwise: bb8];
+//        switchInt(move _4) -> [false: bb6, otherwise: bb7];
 //    }
 //    bb1: {
-//        falseEdges -> [real: bb12, imaginary: bb2];
+//        falseEdges -> [real: bb10, imaginary: bb2];
 //    }
 //    bb2: {
 //        falseEdges -> [real: bb13, imaginary: bb3];
@@ -38,33 +38,35 @@ fn main() {
 //        unreachable;
 //    }
 //    bb6: {
-//        falseEdges -> [real: bb4, imaginary: bb2];
+//        _6 = Le(const 10i32, _1);
+//        switchInt(move _6) -> [false: bb8, otherwise: bb9];
 //    }
 //    bb7: {
-//        _6 = Le(const 10i32, _1);
-//        switchInt(move _6) -> [false: bb9, otherwise: bb10];
+//        _5 = Lt(_1, const 10i32);
+//        switchInt(move _5) -> [false: bb6, otherwise: bb1];
 //    }
 //    bb8: {
-//        _5 = Lt(_1, const 10i32);
-//        switchInt(move _5) -> [false: bb7, otherwise: bb1];
+//        switchInt(_1) -> [-1i32: bb3, otherwise: bb4];
 //    }
 //    bb9: {
-//        switchInt(_1) -> [-1i32: bb3, otherwise: bb4];
+//        _7 = Le(_1, const 20i32);
+//        switchInt(move _7) -> [false: bb8, otherwise: bb2];
 //    }
 //    bb10: {
-//        _7 = Le(_1, const 20i32);
-//        switchInt(move _7) -> [false: bb9, otherwise: bb2];
+//        _8 = &shallow _1;
+//        StorageLive(_9);
+//        _9 = _2;
+//        FakeRead(ForMatchGuard, _8);
+//        switchInt(move _9) -> [false: bb12, otherwise: bb11];
 //    }
 //    bb11: {
+//        StorageDead(_9);
 //        _3 = const 0i32;
 //        goto -> bb16;
 //    }
 //    bb12: {
-//        _8 = &shallow _1;
-//        StorageLive(_9);
-//        _9 = _2;
-//        FakeRead(ForMatchGuard, _8);
-//        switchInt(move _9) -> [false: bb6, otherwise: bb11];
+//        StorageDead(_9);
+//        falseEdges -> [real: bb4, imaginary: bb2];
 //    }
 //    bb13: {
 //        _3 = const 1i32;
@@ -79,7 +81,6 @@ fn main() {
 //        goto -> bb16;
 //    }
 //    bb16: {
-//        StorageDead(_9);
 //        _0 = ();
 //        StorageDead(_2);
 //        StorageDead(_1);
index 8348f9a77467888fe5b7273d7e76790a33f5fad9..6ac9cee79f53a82e53ace7b82f2c3f3a284ac84b 100644 (file)
@@ -19,10 +19,10 @@ fn main() {
 // bb0: {
 //     FakeRead(ForMatchedPlace, _1);
 //     _3 = discriminant(_1);
-//     switchInt(move _3) -> [1isize: bb5, otherwise: bb2];
+//     switchInt(move _3) -> [1isize: bb4, otherwise: bb2];
 // }
 // bb1: {
-//     goto -> bb7;
+//     goto -> bb5;
 // }
 // bb2: {
 //     goto -> bb8;
@@ -31,16 +31,9 @@ fn main() {
 //     unreachable;
 // }
 // bb4: {
-//     goto -> bb2;
-// }
-// bb5: {
 //     switchInt((*(*((_1 as Some).0: &'<empty> &'<empty> i32)))) -> [0i32: bb1, otherwise: bb2];
 // }
-// bb6: {
-//     _0 = const 0i32;
-//     goto -> bb9;
-// }
-// bb7: {
+// bb5: {
 //     _4 = &shallow _1;
 //     _5 = &shallow ((_1 as Some).0: &'<empty> &'<empty> i32);
 //     _6 = &shallow (*((_1 as Some).0: &'<empty> &'<empty> i32));
@@ -51,14 +44,22 @@ fn main() {
 //     FakeRead(ForMatchGuard, _5);
 //     FakeRead(ForMatchGuard, _6);
 //     FakeRead(ForMatchGuard, _7);
-//     switchInt(move _8) -> [false: bb4, otherwise: bb6];
+//     switchInt(move _8) -> [false: bb7, otherwise: bb6];
+// }
+// bb6: {
+//     StorageDead(_8);
+//     _0 = const 0i32;
+//     goto -> bb9;
+// }
+// bb7: {
+//     StorageDead(_8);
+//     goto -> bb2;
 // }
 // bb8: {
 //     _0 = const 1i32;
 //     goto -> bb9;
 // }
 // bb9: {
-//     StorageDead(_8);
 //     return;
 // }
 // bb10 (cleanup): {
@@ -70,10 +71,10 @@ fn main() {
 // bb0: {
 //     nop;
 //     _3 = discriminant(_1);
-//     switchInt(move _3) -> [1isize: bb5, otherwise: bb2];
+//     switchInt(move _3) -> [1isize: bb4, otherwise: bb2];
 // }
 // bb1: {
-//     goto -> bb7;
+//     goto -> bb5;
 // }
 // bb2: {
 //     goto -> bb8;
@@ -82,16 +83,9 @@ fn main() {
 //     unreachable;
 // }
 // bb4: {
-//     goto -> bb2;
-// }
-// bb5: {
 //     switchInt((*(*((_1 as Some).0: &'<empty> &'<empty> i32)))) -> [0i32: bb1, otherwise: bb2];
 // }
-// bb6: {
-//     _0 = const 0i32;
-//     goto -> bb9;
-// }
-// bb7: {
+// bb5: {
 //     nop;
 //     nop;
 //     nop;
@@ -102,14 +96,22 @@ fn main() {
 //     nop;
 //     nop;
 //     nop;
-//     switchInt(move _8) -> [false: bb4, otherwise: bb6];
+//     switchInt(move _8) -> [false: bb7, otherwise: bb6];
+// }
+// bb6: {
+//     StorageDead(_8);
+//     _0 = const 0i32;
+//     goto -> bb9;
+// }
+// bb7: {
+//     StorageDead(_8);
+//     goto -> bb2;
 // }
 // bb8: {
 //     _0 = const 1i32;
 //     goto -> bb9;
 // }
 // bb9: {
-//     StorageDead(_8);
 //     return;
 // }
 // bb10 (cleanup): {
index da0236ac450705418031a3cec5a8faa37337eb97..78609a6e24b5eefd783900ca4485d0d4e01c43ed 100644 (file)
@@ -105,6 +105,14 @@ fn mut_ref_arg(mut arg : &mut [u8]) -> &mut [u8] {
       _ => {}
     }
 
+    // Attribute should be respected on match arms
+    match 0 {
+        #[allow(unused_mut)]
+        mut x => {
+            let mut y = 1;
+        },
+    }
+
     let x = |mut y: isize| y = 32;
     fn nothing(mut foo: isize) { foo = 37; }
 
index e41d8f8ac74083ce9f88a9d84f39d01b25a89477..1a175c9683ec729642b81dae0050c1299e65a14c 100644 (file)
@@ -133,7 +133,7 @@ LL |     fn mut_ref_arg(mut arg : &mut [u8]) -> &mut [u8] {
    |                    help: remove this `mut`
 
 error: variable does not need to be mutable
-  --> $DIR/lint-unused-mut-variables.rs:130:9
+  --> $DIR/lint-unused-mut-variables.rs:138:9
    |
 LL |     let mut b = vec![2];
    |         ----^
@@ -141,7 +141,7 @@ LL |     let mut b = vec![2];
    |         help: remove this `mut`
    |
 note: lint level defined here
-  --> $DIR/lint-unused-mut-variables.rs:126:8
+  --> $DIR/lint-unused-mut-variables.rs:134:8
    |
 LL | #[deny(unused_mut)]
    |        ^^^^^^^^^^