Give match arms a drop/region scope

Also give arms the correct lint scope in MIR.
This commit is contained in:
Matthew Jasper 2019-04-03 19:21:51 +01:00
parent af6a9a2c62
commit f506aea1fa
10 changed files with 300 additions and 146 deletions

View File

@ -419,7 +419,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
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 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
// 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

View File

@ -119,18 +119,18 @@ impl fmt::Debug for Scope {
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 @@ newtype_index! {
///
/// * 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 }`.

View File

@ -12,6 +12,7 @@ use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// 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 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 = 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 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
//
// 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 }
});

View File

@ -19,13 +19,18 @@ paragraph). This is because region scopes are tied to
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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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.

View 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,
}
}

View 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

View 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];
// }
// bb9: {
// switchInt(_1) -> [-1i32: bb3, otherwise: bb4];
// }
// bb10: {
// bb9: {
// _7 = Le(_1, const 20i32);
// switchInt(move _7) -> [false: bb9, otherwise: bb2];
// switchInt(move _7) -> [false: bb8, otherwise: bb2];
// }
// bb11: {
// _3 = const 0i32;
// goto -> bb16;
// }
// bb12: {
// bb10: {
// _8 = &shallow _1;
// StorageLive(_9);
// _9 = _2;
// FakeRead(ForMatchGuard, _8);
// switchInt(move _9) -> [false: bb6, otherwise: bb11];
// switchInt(move _9) -> [false: bb12, otherwise: bb11];
// }
// bb11: {
// StorageDead(_9);
// _3 = const 0i32;
// goto -> bb16;
// }
// bb12: {
// 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);

View 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): {

View File

@ -105,6 +105,14 @@ fn main() {
_ => {}
}
// 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; }

View 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)]
| ^^^^^^^^^^