Also give arms the correct lint scope in MIR.
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
// 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
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)
}
///
/// * 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);
}
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 {
}
intravisit::walk_arm(visitor, arm);
+
+ visitor.cx = prev_cx;
}
fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &'tcx hir::Pat) {
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 }`.
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;
// 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
for arm_block in arm_end_blocks {
self.cfg.terminate(
- arm_block,
+ unpack!(arm_block),
outer_source_info,
TerminatorKind::Goto { target: end_block },
);
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,
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);
//
// 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 }
});
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
// 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 {
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.
_ => 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,
}
}
// _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
// 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
// _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];
// 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;
// _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);
// 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];
// 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
// 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];
// 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;
// goto -> bb16;
// }
// bb16: {
-// StorageDead(_9);
// _0 = ();
// StorageDead(_2);
// StorageDead(_1);
// 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;
// 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));
// 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): {
// 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;
// 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;
// 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): {
_ => {}
}
+ // 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; }
| 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];
| ----^
| 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)]
| ^^^^^^^^^^