From f506aea1fac0977c7215b4240f4d99b45bf7ae97 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 3 Apr 2019 19:21:51 +0100 Subject: [PATCH] Give match arms a drop/region scope Also give arms the correct lint scope in MIR. --- src/librustc/cfg/construct.rs | 10 +- src/librustc/middle/region.rs | 34 +++-- src/librustc_mir/build/matches/mod.rs | 87 +++++++----- src/librustc_mir/build/scope.rs | 84 +++++++++++- src/librustc_mir/hair/cx/expr.rs | 8 +- src/test/mir-opt/match_false_edges.rs | 126 +++++++++++------- src/test/mir-opt/match_test.rs | 33 ++--- src/test/mir-opt/remove_fake_borrows.rs | 50 +++---- src/test/ui/lint/lint-unused-mut-variables.rs | 8 ++ .../ui/lint/lint-unused-mut-variables.stderr | 4 +- 10 files changed, 299 insertions(+), 145 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 2e54165be1f..ef0d4be268e 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -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 diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 37681ad7fcd..fa4e8e3d476 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -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 }`. diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 93a702dc44e..091e39630d6 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -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 } }); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 471304012c9..0d1d40a8af6 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -19,13 +19,18 @@ 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. diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 50140880a36..d623f149988 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -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, } } diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 7ac36a22274..0850c552536 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -45,13 +45,13 @@ fn main() { // _2 = std::option::Option::::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) 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::::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::::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 diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index a5317f98ef1..2ef9520c12c 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -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); diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index 8348f9a7746..6ac9cee79f5 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -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: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb6: { -// _0 = const 0i32; -// goto -> bb9; -// } -// bb7: { +// bb5: { // _4 = &shallow _1; // _5 = &shallow ((_1 as Some).0: &' &' i32); // _6 = &shallow (*((_1 as Some).0: &' &' 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: &' &' 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): { diff --git a/src/test/ui/lint/lint-unused-mut-variables.rs b/src/test/ui/lint/lint-unused-mut-variables.rs index da0236ac450..78609a6e24b 100644 --- a/src/test/ui/lint/lint-unused-mut-variables.rs +++ b/src/test/ui/lint/lint-unused-mut-variables.rs @@ -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; } diff --git a/src/test/ui/lint/lint-unused-mut-variables.stderr b/src/test/ui/lint/lint-unused-mut-variables.stderr index e41d8f8ac74..1a175c9683e 100644 --- a/src/test/ui/lint/lint-unused-mut-variables.stderr +++ b/src/test/ui/lint/lint-unused-mut-variables.stderr @@ -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)] | ^^^^^^^^^^ -- 2.44.0