]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #60174 - matthewjasper:add-match-arm-scopes, r=pnkfelix
authorbors <bors@rust-lang.org>
Thu, 23 May 2019 04:48:21 +0000 (04:48 +0000)
committerbors <bors@rust-lang.org>
Thu, 23 May 2019 04:48:21 +0000 (04:48 +0000)
Add match arm scopes and other scope fixes

* Add drop and lint scopes for match arms.
* Lint attributes are now respected on match arms.
* Make sure we emit a StorageDead if we diverge when initializing a temporary.
* Adjust MIR pretty printing of scopes for locals.
* Don't generate duplicate lint scopes for `let statements`.
* Add some previously missing fake borrows for matches.

closes #46525

cc @rust-lang/compiler

41 files changed:
src/librustc/cfg/construct.rs
src/librustc/hir/intravisit.rs
src/librustc/hir/lowering.rs
src/librustc/hir/map/collector.rs
src/librustc/hir/map/mod.rs
src/librustc/hir/mod.rs
src/librustc/hir/print.rs
src/librustc/lint/mod.rs
src/librustc/middle/region.rs
src/librustc_mir/build/block.rs
src/librustc_mir/build/expr/as_operand.rs
src/librustc_mir/build/expr/as_place.rs
src/librustc_mir/build/expr/as_rvalue.rs
src/librustc_mir/build/expr/as_temp.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/expr/stmt.rs
src/librustc_mir/build/matches/mod.rs
src/librustc_mir/build/mod.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/dataflow/impls/mod.rs
src/librustc_mir/hair/cx/expr.rs
src/librustc_mir/hair/mod.rs
src/librustc_passes/hir_stats.rs
src/librustc_typeck/check/_match.rs
src/libsyntax/ast.rs
src/libsyntax/ext/build.rs
src/libsyntax/mut_visit.rs
src/libsyntax/parse/parser.rs
src/test/mir-opt/box_expr.rs
src/test/mir-opt/issue-41110.rs
src/test/mir-opt/issue-49232.rs
src/test/mir-opt/match-arm-scopes.rs [new file with mode: 0644]
src/test/mir-opt/match_false_edges.rs
src/test/mir-opt/match_test.rs
src/test/mir-opt/packed-struct-drop-aligned.rs
src/test/mir-opt/remove_fake_borrows.rs
src/test/mir-opt/storage_live_dead_in_statics.rs
src/test/ui/lint/lint-match-arms.rs [new file with mode: 0644]
src/test/ui/lint/lint-match-arms.stderr [new file with mode: 0644]
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 38d6d710868c08919821de9caf2b34a05cb2a761..517c99f99efea1e257339c96ca398c644e1b68a5 100644 (file)
@@ -1102,6 +1102,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
 }
 
 pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) {
+    visitor.visit_id(arm.hir_id);
     walk_list!(visitor, visit_pat, &arm.pats);
     if let Some(ref g) = arm.guard {
         match g {
index 90dff5e3fc4b44fdd23f2044e73acfe796e426ea..6b82548f6dcde11e7ee9ce2ea6f397647c7e0935 100644 (file)
@@ -1314,6 +1314,7 @@ fn lower_token(&mut self, token: Token, span: Span) -> TokenStream {
 
     fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
         hir::Arm {
+            hir_id: self.next_id(),
             attrs: self.lower_attrs(&arm.attrs),
             pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
             guard: match arm.guard {
@@ -1321,6 +1322,7 @@ fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
                 _ => None,
             },
             body: P(self.lower_expr(&arm.body)),
+            span: arm.span,
         }
     }
 
@@ -5024,9 +5026,11 @@ fn lower_trait_bound_modifier(&mut self, f: TraitBoundModifier) -> hir::TraitBou
 
     fn arm(&mut self, pats: hir::HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
         hir::Arm {
+            hir_id: self.next_id(),
             attrs: hir_vec![],
             pats,
             guard: None,
+            span: expr.span,
             body: expr,
         }
     }
index eeba628b3bf2168ad943ac41fe4cca076ed5bf72..b5203f9ec1f72b030f68ce025ec7f68d3c273e06 100644 (file)
@@ -430,6 +430,16 @@ fn visit_pat(&mut self, pat: &'hir Pat) {
         });
     }
 
+    fn visit_arm(&mut self, arm: &'hir Arm) {
+        let node = Node::Arm(arm);
+
+        self.insert(arm.span, arm.hir_id, node);
+
+        self.with_parent(arm.hir_id, |this| {
+            intravisit::walk_arm(this, arm);
+        });
+    }
+
     fn visit_anon_const(&mut self, constant: &'hir AnonConst) {
         self.insert(DUMMY_SP, constant.hir_id, Node::AnonConst(constant));
 
index e5ca97b82ab4826f5533a35eddea2469d4a8c040..0741d9322c65328eff9d2fb7dcb1ffdac3883481 100644 (file)
@@ -373,6 +373,7 @@ fn def_kind(&self, node_id: NodeId) -> Option<DefKind> {
             Node::Pat(_) |
             Node::Binding(_) |
             Node::Local(_) |
+            Node::Arm(_) |
             Node::Lifetime(_) |
             Node::Visibility(_) |
             Node::Block(_) |
@@ -1000,6 +1001,7 @@ pub fn attrs_by_hir_id(&self, id: HirId) -> &'hir [ast::Attribute] {
             Some(Node::Field(ref f)) => Some(&f.attrs[..]),
             Some(Node::Expr(ref e)) => Some(&*e.attrs),
             Some(Node::Stmt(ref s)) => Some(s.node.attrs()),
+            Some(Node::Arm(ref a)) => Some(&*a.attrs),
             Some(Node::GenericParam(param)) => Some(&param.attrs[..]),
             // Unit/tuple structs/variants take the attributes straight from
             // the struct/variant definition.
@@ -1073,6 +1075,7 @@ pub fn span_by_hir_id(&self, hir_id: HirId) -> Span {
             Some(Node::TraitRef(tr)) => tr.path.span,
             Some(Node::Binding(pat)) => pat.span,
             Some(Node::Pat(pat)) => pat.span,
+            Some(Node::Arm(arm)) => arm.span,
             Some(Node::Block(block)) => block.span,
             Some(Node::Ctor(..)) => match self.find_by_hir_id(
                 self.get_parent_node_by_hir_id(hir_id))
@@ -1288,6 +1291,7 @@ pub fn print_node(&mut self, node: Node<'_>) -> io::Result<()> {
             Node::TraitRef(a)     => self.print_trait_ref(&a),
             Node::Binding(a)      |
             Node::Pat(a)          => self.print_pat(&a),
+            Node::Arm(a)          => self.print_arm(&a),
             Node::Block(a)        => {
                 use syntax::print::pprust::PrintState;
 
@@ -1417,6 +1421,9 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId, include_id: bool) -> String {
         Some(Node::Pat(_)) => {
             format!("pat {}{}", map.hir_to_pretty_string(id), id_str)
         }
+        Some(Node::Arm(_)) => {
+            format!("arm {}{}", map.hir_to_pretty_string(id), id_str)
+        }
         Some(Node::Block(_)) => {
             format!("block {}{}", map.hir_to_pretty_string(id), id_str)
         }
index e3e930f5d1d3c656e93e2c1c155abd909477a438..2ae5f7a0b5531f4771c6c264320cb75fe3c6f71e 100644 (file)
@@ -1228,6 +1228,9 @@ pub struct Local {
 /// `<pats> (if <guard>) => <body>`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
 pub struct Arm {
+    #[stable_hasher(ignore)]
+    pub hir_id: HirId,
+    pub span: Span,
     pub attrs: HirVec<Attribute>,
     /// Multiple patterns can be combined with `|`
     pub pats: HirVec<P<Pat>>,
@@ -2656,6 +2659,7 @@ pub enum Node<'hir> {
     TraitRef(&'hir TraitRef),
     Binding(&'hir Pat),
     Pat(&'hir Pat),
+    Arm(&'hir Arm),
     Block(&'hir Block),
     Local(&'hir Local),
     MacroDef(&'hir MacroDef),
index 60511ecc883ce5aaf42d8bf447213596e48d218a..e51681082f3192ed33c6c2e420258c39ea6ba6f5 100644 (file)
@@ -1862,7 +1862,7 @@ pub fn print_pat(&mut self, pat: &hir::Pat) -> io::Result<()> {
         self.ann.post(self, AnnNode::Pat(pat))
     }
 
-    fn print_arm(&mut self, arm: &hir::Arm) -> io::Result<()> {
+    pub fn print_arm(&mut self, arm: &hir::Arm) -> io::Result<()> {
         // I have no idea why this check is necessary, but here it
         // is :(
         if arm.attrs.is_empty() {
index 9c4683e094634eea384d348ff8c2b9785a6c6dfe..512e4d434434ce892a52f9050036e9ba0ac8359e 100644 (file)
@@ -852,6 +852,12 @@ fn visit_local(&mut self, l: &'tcx hir::Local) {
         })
     }
 
+    fn visit_arm(&mut self, a: &'tcx hir::Arm) {
+        self.with_lint_attrs(a.hir_id, &a.attrs, |builder| {
+            intravisit::walk_arm(builder, a);
+        })
+    }
+
     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
         self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |builder| {
             intravisit::walk_trait_item(builder, trait_item);
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 7469aceee3a9ee959c7647fee82007ba20eaed77..b5bab1585342a1cacf4329ee69aacafcf31d31e1 100644 (file)
@@ -23,8 +23,8 @@ pub fn ast_block(&mut self,
             safety_mode
         } =
             self.hir.mirror(ast_block);
-        self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), block, move |this| {
-            this.in_scope((region_scope, source_info), LintLevel::Inherited, block, move |this| {
+        self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), move |this| {
+            this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
                 if targeted_by_break {
                     // This is a `break`-able block
                     let exit_block = this.cfg.start_new_block();
@@ -83,9 +83,9 @@ fn ast_block_stmts(&mut self,
                 StmtKind::Expr { scope, expr } => {
                     this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
                     unpack!(block = this.in_opt_scope(
-                        opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
+                        opt_destruction_scope.map(|de|(de, source_info)), |this| {
                             let si = (scope, source_info);
-                            this.in_scope(si, LintLevel::Inherited, block, |this| {
+                            this.in_scope(si, LintLevel::Inherited, |this| {
                                 let expr = this.hir.mirror(expr);
                                 this.stmt_expr(block, expr, Some(stmt_span))
                             })
@@ -113,31 +113,39 @@ fn ast_block_stmts(&mut self,
                     let remainder_span = remainder_scope.span(this.hir.tcx(),
                                                               &this.hir.region_scope_tree);
 
-                    let scope;
+                    let visibility_scope =
+                        Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
 
                     // Evaluate the initializer, if present.
                     if let Some(init) = initializer {
                         let initializer_span = init.span();
 
-                        scope = this.declare_bindings(
-                            None,
-                            remainder_span,
-                            lint_level,
-                            &pattern,
-                            ArmHasGuard(false),
-                            Some((None, initializer_span)),
-                        );
                         unpack!(block = this.in_opt_scope(
-                            opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
+                            opt_destruction_scope.map(|de|(de, source_info)), |this| {
                                 let scope = (init_scope, source_info);
-                                this.in_scope(scope, lint_level, block, |this| {
+                                this.in_scope(scope, lint_level, |this| {
+                                    this.declare_bindings(
+                                        visibility_scope,
+                                        remainder_span,
+                                        &pattern,
+                                        ArmHasGuard(false),
+                                        Some((None, initializer_span)),
+                                    );
                                     this.expr_into_pattern(block, pattern, init)
                                 })
                             }));
                     } else {
-                        scope = this.declare_bindings(
-                            None, remainder_span, lint_level, &pattern,
-                            ArmHasGuard(false), None);
+                        let scope = (init_scope, source_info);
+                        unpack!(this.in_scope(scope, lint_level, |this| {
+                            this.declare_bindings(
+                                visibility_scope,
+                                remainder_span,
+                                &pattern,
+                                ArmHasGuard(false),
+                                None,
+                            );
+                            block.unit()
+                        }));
 
                         debug!("ast_block_stmts: pattern={:?}", pattern);
                         this.visit_bindings(
@@ -149,8 +157,8 @@ fn ast_block_stmts(&mut self,
                             })
                     }
 
-                    // Enter the source scope, after evaluating the initializer.
-                    if let Some(source_scope) = scope {
+                    // Enter the visibility scope, after evaluating the initializer.
+                    if let Some(source_scope) = visibility_scope {
                         this.source_scope = source_scope;
                     }
                 }
index e354a2ee8160b47f1f17c40b8561df9eb14e82e4..ed80cb1a16369e6286917c16d0dfe05d2a54031f 100644 (file)
@@ -57,7 +57,7 @@ fn expr_as_operand(
         {
             let source_info = this.source_info(expr.span);
             let region_scope = (region_scope, source_info);
-            return this.in_scope(region_scope, lint_level, block, |this| {
+            return this.in_scope(region_scope, lint_level, |this| {
                 this.as_operand(block, scope, value)
             });
         }
index 5f444d4ceeb8895fe40bae33b69969bad3ac3942..a956eacb0699fd3cd1276a0687645addc1282bf6 100644 (file)
@@ -52,7 +52,7 @@ fn expr_as_place(
                 region_scope,
                 lint_level,
                 value,
-            } => this.in_scope((region_scope, source_info), lint_level, block, |this| {
+            } => this.in_scope((region_scope, source_info), lint_level, |this| {
                 if mutability == Mutability::Not {
                     this.as_read_only_place(block, value)
                 } else {
index fbc4835a6557b4e2a7b34f744286a8c2c424fd72..a0b504a99de9a7981778362410688bec60663dd7 100644 (file)
@@ -58,7 +58,7 @@ fn expr_as_rvalue(
                 value,
             } => {
                 let region_scope = (region_scope, source_info);
-                this.in_scope(region_scope, lint_level, block, |this| {
+                this.in_scope(region_scope, lint_level, |this| {
                     this.as_rvalue(block, scope, value)
                 })
             }
index cba771f27065d93cd6649f94ee44bfccfd3a2a43..cffd8fb2892f5ce987d1946a431f998a1669dade 100644 (file)
@@ -1,6 +1,7 @@
 //! See docs in build/expr/mod.rs
 
 use crate::build::{BlockAnd, BlockAndExtension, Builder};
+use crate::build::scope::{CachedBlock, DropKind};
 use crate::hair::*;
 use rustc::middle::region;
 use rustc::mir::*;
@@ -43,7 +44,7 @@ fn expr_as_temp(
             value,
         } = expr.kind
         {
-            return this.in_scope((region_scope, source_info), lint_level, block, |this| {
+            return this.in_scope((region_scope, source_info), lint_level, |this| {
                 this.as_temp(block, temp_lifetime, value, mutability)
             });
         }
@@ -63,6 +64,8 @@ fn expr_as_temp(
             }
             this.local_decls.push(local_decl)
         };
+        let temp_place = &Place::Base(PlaceBase::Local(temp));
+
         if !expr_ty.is_never() {
             this.cfg.push(
                 block,
@@ -71,25 +74,38 @@ fn expr_as_temp(
                     kind: StatementKind::StorageLive(temp),
                 },
             );
+
+            // In constants, `temp_lifetime` is `None` for temporaries that live for the
+            // `'static` lifetime. Thus we do not drop these temporaries and simply leak them.
+            // This is equivalent to what `let x = &foo();` does in functions. The temporary
+            // is lifted to their surrounding scope. In a function that means the temporary lives
+            // until just before the function returns. In constants that means it outlives the
+            // constant's initialization value computation. Anything outliving a constant
+            // must have the `'static` lifetime and live forever.
+            // Anything with a shorter lifetime (e.g the `&foo()` in `bar(&foo())` or anything
+            // within a block will keep the regular drops just like runtime code.
+            if let Some(temp_lifetime) = temp_lifetime {
+                this.schedule_drop(
+                    expr_span,
+                    temp_lifetime,
+                    temp_place,
+                    expr_ty,
+                    DropKind::Storage,
+                );
+            }
         }
 
-        unpack!(block = this.into(&Place::Base(PlaceBase::Local(temp)), block, expr));
+        unpack!(block = this.into(temp_place, block, expr));
 
-        // In constants, temp_lifetime is None for temporaries that live for the
-        // 'static lifetime. Thus we do not drop these temporaries and simply leak them.
-        // This is equivalent to what `let x = &foo();` does in functions. The temporary
-        // is lifted to their surrounding scope. In a function that means the temporary lives
-        // until just before the function returns. In constants that means it outlives the
-        // constant's initialization value computation. Anything outliving a constant
-        // must have the `'static` lifetime and live forever.
-        // Anything with a shorter lifetime (e.g the `&foo()` in `bar(&foo())` or anything
-        // within a block will keep the regular drops just like runtime code.
         if let Some(temp_lifetime) = temp_lifetime {
-            this.schedule_drop_storage_and_value(
+            this.schedule_drop(
                 expr_span,
                 temp_lifetime,
-                &Place::Base(PlaceBase::Local(temp)),
+                temp_place,
                 expr_ty,
+                DropKind::Value {
+                    cached_block: CachedBlock::default(),
+                },
             );
         }
 
index 15795a64e3b7dcea2703280b12c3fc6024ff40dc..7bdfdf0b0895f9579fe4a0cd16cd9f2ae809ebf6 100644 (file)
@@ -46,7 +46,7 @@ pub fn into_expr(
                 value,
             } => {
                 let region_scope = (region_scope, source_info);
-                this.in_scope(region_scope, lint_level, block, |this| {
+                this.in_scope(region_scope, lint_level, |this| {
                     this.into(destination, block, value)
                 })
             }
index b58914b017fd4601f4a677772ed2fa9d93c0ac17..ac690f89264bfdd0d84f163ecbc3f79df3534733 100644 (file)
@@ -29,7 +29,7 @@ pub fn stmt_expr(&mut self,
                 value,
             } => {
                 let value = this.hir.mirror(value);
-                this.in_scope((region_scope, source_info), lint_level, block, |this| {
+                this.in_scope((region_scope, source_info), lint_level, |this| {
                     this.stmt_expr(block, value, opt_stmt_span)
                 })
             }
index 51d5c96083d8f61b6b15b28b9a5278cae7242372..58ca35abcb123ddb5f8cbec5470d3e652b80325f 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,
-                LintLevel::Inherited,
-                &arm.patterns[0],
-                ArmHasGuard(arm.guard.is_some()),
-                Some((Some(&scrutinee_place), scrutinee_span)),
-            );
-
-            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 {
-                self.source_scope = source_scope;
-            }
+                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 },
             );
@@ -489,33 +492,20 @@ pub fn declare_bindings(
         &mut self,
         mut visibility_scope: Option<SourceScope>,
         scope_span: Span,
-        lint_level: LintLevel,
         pattern: &Pattern<'tcx>,
         has_guard: ArmHasGuard,
         opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
     ) -> Option<SourceScope> {
-        assert!(
-            !(visibility_scope.is_some() && lint_level.is_explicit()),
-            "can't have both a visibility and a lint scope at the same time"
-        );
-        let mut scope = self.source_scope;
         debug!("declare_bindings: pattern={:?}", pattern);
         self.visit_bindings(
             &pattern,
             UserTypeProjections::none(),
             &mut |this, mutability, name, mode, var, span, ty, user_ty| {
                 if visibility_scope.is_none() {
-                    // If we have lints, create a new source scope
-                    // that marks the lints for the locals. See the comment
-                    // on the `source_info` field for why this is needed.
-                    if lint_level.is_explicit() {
-                        scope = this.new_source_scope(scope_span, lint_level, None);
-                    }
-                visibility_scope = Some(this.new_source_scope(scope_span,
-                                                           LintLevel::Inherited,
-                                                           None));
+                    visibility_scope =
+                        Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
                 }
-                let source_info = SourceInfo { span, scope };
+                let source_info = SourceInfo { span, scope: this.source_scope };
                 let visibility_scope = visibility_scope.unwrap();
                 this.declare_binding(
                     source_info,
@@ -880,7 +870,7 @@ fn match_candidates<'pat>(
             span,
             untested_candidates,
             join_block,
-            &mut None,
+            fake_borrows,
         )
     }
 
@@ -1328,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);
 
@@ -1510,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 199b0176601e19fc01f0978000ce584ff84f2564..55b5d5d1471ac0ff8907c407f57835134ee368e9 100644 (file)
@@ -704,13 +704,13 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
     let mut block = START_BLOCK;
     let source_info = builder.source_info(span);
     let call_site_s = (call_site_scope, source_info);
-    unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| {
+    unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
         if should_abort_on_panic(tcx, fn_def_id, abi) {
             builder.schedule_abort();
         }
 
         let arg_scope_s = (arg_scope, source_info);
-        unpack!(block = builder.in_scope(arg_scope_s, LintLevel::Inherited, block, |builder| {
+        unpack!(block = builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
             builder.args_and_body(block, &arguments, arg_scope, &body.value)
         }));
         // Attribute epilogue to function's closing brace
@@ -950,10 +950,13 @@ fn args_and_body(&mut self,
                         self.var_indices.insert(var, LocalsForNode::One(local));
                     }
                     _ => {
-                        scope = self.declare_bindings(scope, ast_body.span,
-                                                      LintLevel::Inherited, &pattern,
-                                                      matches::ArmHasGuard(false),
-                                                      Some((Some(&place), span)));
+                        scope = self.declare_bindings(
+                            scope,
+                            ast_body.span,
+                            &pattern,
+                            matches::ArmHasGuard(false),
+                            Some((Some(&place), span)),
+                        );
                         unpack!(block = self.place_into_pattern(block, pattern, &place, false));
                     }
                 }
index 3b11e335fb87f6019d901d259a0d22e15395db50..58339173c9e5d2590e446be852d77d9dbbc22dd0 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
@@ -282,13 +287,13 @@ pub fn in_breakable_scope<F, R>(&mut self,
 
     pub fn in_opt_scope<F, R>(&mut self,
                               opt_scope: Option<(region::Scope, SourceInfo)>,
-                              mut block: BasicBlock,
                               f: F)
                               -> BlockAnd<R>
         where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
     {
-        debug!("in_opt_scope(opt_scope={:?}, block={:?})", opt_scope, block);
+        debug!("in_opt_scope(opt_scope={:?})", opt_scope);
         if let Some(region_scope) = opt_scope { self.push_scope(region_scope); }
+        let mut block;
         let rv = unpack!(block = f(self));
         if let Some(region_scope) = opt_scope {
             unpack!(block = self.pop_scope(region_scope, block));
@@ -302,12 +307,11 @@ pub fn in_opt_scope<F, R>(&mut self,
     pub fn in_scope<F, R>(&mut self,
                           region_scope: (region::Scope, SourceInfo),
                           lint_level: LintLevel,
-                          mut block: BasicBlock,
                           f: F)
                           -> BlockAnd<R>
         where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
     {
-        debug!("in_scope(region_scope={:?}, block={:?})", region_scope, block);
+        debug!("in_scope(region_scope={:?})", region_scope);
         let source_scope = self.source_scope;
         let tcx = self.hir.tcx();
         if let LintLevel::Explicit(current_hir_id) = lint_level {
@@ -333,6 +337,7 @@ pub fn in_scope<F, R>(&mut self,
             }
         }
         self.push_scope(region_scope);
+        let mut block;
         let rv = unpack!(block = f(self));
         unpack!(block = self.pop_scope(region_scope, block));
         self.source_scope = source_scope;
@@ -730,7 +735,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 {
@@ -872,6 +877,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 4dcfb3f1a7fc32a9d01dda14d6bc64848ab451b3..03d55b84f32e260f38dce78551558c77388c331f 100644 (file)
@@ -11,8 +11,7 @@
 
 use crate::util::elaborate_drops::DropFlagState;
 
-use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex};
-use super::move_paths::{LookupResult, InitKind};
+use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex, InitKind};
 use super::{BitDenotation, BlockSets, InitialFlow};
 
 use super::drop_flag_effects_for_function_entry;
@@ -470,35 +469,13 @@ fn statement_effect(&self,
         sets.gen_all(&init_loc_map[location]);
 
         match stmt.kind {
-            mir::StatementKind::StorageDead(local) |
-            mir::StatementKind::StorageLive(local) => {
-                // End inits for StorageDead and StorageLive, so that an immutable
-                // variable can be reinitialized on the next iteration of the loop.
-                //
-                // FIXME(#46525): We *need* to do this for StorageLive as well as
-                // StorageDead, because lifetimes of match bindings with guards are
-                // weird - i.e., this code
-                //
-                // ```
-                //     fn main() {
-                //         match 0 {
-                //             a | a
-                //             if { println!("a={}", a); false } => {}
-                //             _ => {}
-                //         }
-                //     }
-                // ```
-                //
-                // runs the guard twice, using the same binding for `a`, and only
-                // storagedeads after everything ends, so if we don't regard the
-                // storagelive as killing storage, we would have a multiple assignment
-                // to immutable data error.
-                if let LookupResult::Exact(mpi) =
-                    rev_lookup.find(&mir::Place::Base(mir::PlaceBase::Local(local))) {
-                    debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
-                           stmt, location, &init_path_map[mpi]);
-                    sets.kill_all(&init_path_map[mpi]);
-                }
+            mir::StatementKind::StorageDead(local) => {
+                // End inits for StorageDead, so that an immutable variable can
+                // be reinitialized on the next iteration of the loop.
+                let move_path_index = rev_lookup.find_local(local);
+                debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
+                        stmt, location, &init_path_map[move_path_index]);
+                sets.kill_all(&init_path_map[move_path_index]);
             }
             _ => {}
         }
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 d4f139e103a641a12d665f0e8cf05ec2e96f0255..8e19913f4df26d30ab8611cef310842cce27677f 100644 (file)
@@ -31,15 +31,6 @@ pub enum LintLevel {
     Explicit(hir::HirId)
 }
 
-impl LintLevel {
-    pub fn is_explicit(self) -> bool {
-        match self {
-            LintLevel::Inherited => false,
-            LintLevel::Explicit(_) => true
-        }
-    }
-}
-
 #[derive(Clone, Debug)]
 pub struct Block<'tcx> {
     pub targeted_by_break: bool,
@@ -311,6 +302,8 @@ pub struct Arm<'tcx> {
     pub guard: Option<Guard<'tcx>>,
     pub body: ExprRef<'tcx>,
     pub lint_level: LintLevel,
+    pub scope: region::Scope,
+    pub span: Span,
 }
 
 #[derive(Clone, Debug)]
index c74314ce0c4b509aea306cc743dc09def8520249..0088c97679c6666f13e1d05731a1cdf2ebfb3779 100644 (file)
@@ -149,7 +149,7 @@ fn visit_stmt(&mut self, s: &'v hir::Stmt) {
     }
 
     fn visit_arm(&mut self, a: &'v hir::Arm) {
-        self.record("Arm", Id::None, a);
+        self.record("Arm", Id::Node(a.hir_id), a);
         hir_visit::walk_arm(self, a)
     }
 
index a69f639e8941f51d3aca9290fd553f7b7e27551b..e4b431e6e68f17bc653196b4818d043680b3d13c 100644 (file)
@@ -540,7 +540,7 @@ fn borrow_pat_suggestion(
                         err.help(&format!("did you mean `{}: &{}`?", snippet, expected));
                     }
                 }
-                hir::Node::Expr(hir::Expr { node: hir::ExprKind::Match(..), .. }) |
+                hir::Node::Arm(_) |
                 hir::Node::Pat(_) => {
                     // rely on match ergonomics or it might be nested `&&pat`
                     if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
@@ -781,14 +781,17 @@ fn if_fallback_coercion(
     fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> {
         use hir::Node::{Block, Item, Local};
 
-        let node = self.tcx.hir().get_by_hir_id(self.tcx.hir().get_parent_node_by_hir_id(
-            self.tcx.hir().get_parent_node_by_hir_id(hir_id),
-        ));
+        let hir = self.tcx.hir();
+        let arm_id = hir.get_parent_node_by_hir_id(hir_id);
+        let match_id = hir.get_parent_node_by_hir_id(arm_id);
+        let containing_id = hir.get_parent_node_by_hir_id(match_id);
+
+        let node = hir.get_by_hir_id(containing_id);
         if let Block(block) = node {
             // check that the body's parent is an fn
-            let parent = self.tcx.hir().get_by_hir_id(
-                self.tcx.hir().get_parent_node_by_hir_id(
-                    self.tcx.hir().get_parent_node_by_hir_id(block.hir_id),
+            let parent = hir.get_by_hir_id(
+                hir.get_parent_node_by_hir_id(
+                    hir.get_parent_node_by_hir_id(block.hir_id),
                 ),
             );
             if let (Some(expr), Item(hir::Item {
index 0df00e7f4416df0471a260fe4c27f40aea1648b0..064459e750fbeb20b000811795cb934ad8a578ca 100644 (file)
@@ -908,6 +908,7 @@ pub struct Arm {
     pub pats: Vec<P<Pat>>,
     pub guard: Option<Guard>,
     pub body: P<Expr>,
+    pub span: Span,
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
index 466715e69fd565595b733b0da37a5c27f03d656f..ad8fb12deb7f6b34cd33954514ccc0b8a3a277da 100644 (file)
@@ -890,12 +890,13 @@ fn pat_err(&self, span: Span, pat: P<ast::Pat>) -> P<ast::Pat> {
         self.pat_tuple_struct(span, path, vec![pat])
     }
 
-    fn arm(&self, _span: Span, pats: Vec<P<ast::Pat>>, expr: P<ast::Expr>) -> ast::Arm {
+    fn arm(&self, span: Span, pats: Vec<P<ast::Pat>>, expr: P<ast::Expr>) -> ast::Arm {
         ast::Arm {
             attrs: vec![],
             pats,
             guard: None,
             body: expr,
+            span,
         }
     }
 
index a6564de3b982daa9ef7ced6f84abd3982e793e5f..e743248ef4b59057a10b5c3b7b2150098b58e7b2 100644 (file)
@@ -391,11 +391,15 @@ pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
     vis.visit_span(span);
 }
 
-pub fn noop_visit_arm<T: MutVisitor>(Arm { attrs, pats, guard, body }: &mut Arm, vis: &mut T) {
+pub fn noop_visit_arm<T: MutVisitor>(
+    Arm { attrs, pats, guard, body, span }: &mut Arm,
+    vis: &mut T,
+) {
     visit_attrs(attrs, vis);
     visit_vec(pats, |pat| vis.visit_pat(pat));
     visit_opt(guard, |guard| vis.visit_guard(guard));
     vis.visit_expr(body);
+    vis.visit_span(span);
 }
 
 pub fn noop_visit_guard<T: MutVisitor>(g: &mut Guard, vis: &mut T) {
index 6c1f1d51a074151474cbd361a305129948d7b381..7600d6078a106cebd3ef1124e89f86eb4c05a586 100644 (file)
@@ -3946,6 +3946,7 @@ fn parse_match_expr(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<E
 
     crate fn parse_arm(&mut self) -> PResult<'a, Arm> {
         let attrs = self.parse_outer_attributes()?;
+        let lo = self.span;
         let pats = self.parse_pats()?;
         let guard = if self.eat_keyword(kw::If) {
             Some(Guard::If(self.parse_expr()?))
@@ -3965,6 +3966,8 @@ fn parse_match_expr(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<E
         let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
             && self.token != token::CloseDelim(token::Brace);
 
+        let hi = self.span;
+
         if require_comma {
             let cm = self.sess.source_map();
             self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)])
@@ -4008,6 +4011,7 @@ fn parse_match_expr(&mut self, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<E
             pats,
             guard,
             body: expr,
+            span: lo.to(hi),
         })
     }
 
index d4852db6d475e39736104ebc1de375729077b62e..ee6adfefe3e360b3a0ec00bd878d1fceb2de619e 100644 (file)
@@ -22,13 +22,11 @@ fn drop(&mut self) {
 // END RUST SOURCE
 // START rustc.main.ElaborateDrops.before.mir
 //     let mut _0: ();
+//     let _1: std::boxed::Box<S>;
 //     let mut _2: std::boxed::Box<S>;
 //     let mut _3: ();
 //     let mut _4: std::boxed::Box<S>;
 //     scope 1 {
-//         let _1: std::boxed::Box<S>;
-//     }
-//     scope 2 {
 //     }
 //     bb0: {
 //         StorageLive(_1);
index 023440af0eb10d014549346c9008271b594b7dbe..0b678be2ab3197ea4f8c1b34b6204c546e16f7fa 100644 (file)
@@ -29,27 +29,24 @@ fn other(self, s: Self) {}
 // END RUST SOURCE
 // START rustc.main.ElaborateDrops.after.mir
 //    let mut _0: ();
+//    let _1: ();
 //    let mut _2: S;
 //    let mut _3: S;
 //    let mut _4: S;
 //    let mut _5: bool;
 //    scope 1 {
-//        let _1: ();
-//    }
-//    scope 2 {
 //    }
 //    ...
 //    bb0: {
 // END rustc.main.ElaborateDrops.after.mir
 // START rustc.test.ElaborateDrops.after.mir
 //    let mut _0: ();
+//    let _1: S;
 //    let mut _3: ();
 //    let mut _4: S;
 //    let mut _5: S;
 //    let mut _6: bool;
 //    ...
-//    let _1: S;
-//    ...
 //    let mut _2: S;
 //    ...
 //    bb0: {
index 29446d2ecc23e5d9180ee6f47957cbde30d42977..bf22f00b5055241febebe48805840a533a8cb617 100644 (file)
@@ -18,14 +18,12 @@ fn main() {
 // fn main() -> (){
 //     let mut _0: ();
 //     let mut _1: ();
+//     let _2: i32;
 //     let mut _3: bool;
 //     let mut _4: !;
 //     let mut _5: ();
 //     let mut _6: &i32;
 //     scope 1 {
-//         let _2: i32;
-//     }
-//     scope 2 {
 //     }
 //     bb0: {
 //         goto -> bb1;
@@ -88,7 +86,6 @@ fn main() {
 //         unreachable;
 //     }
 //     bb17: {
-//         StorageDead(_4);
 //         goto -> bb18;
 //     }
 //     bb18: {
diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs
new file mode 100644 (file)
index 0000000..0f026b8
--- /dev/null
@@ -0,0 +1,245 @@
+// Test that StorageDead and Drops are generated properly for bindings in
+// matches:
+// * The MIR should only contain a single drop of `s` and `t`: at the end
+//   of their respective arms.
+// * StorageDead and StorageLive statements are correctly matched up on
+//   non-unwind paths.
+// * The visibility scopes of the match arms should be disjoint, and contain.
+//   all of the bindings for that scope.
+// * No drop flags are used.
+
+#![feature(nll, bind_by_move_pattern_guards)]
+
+fn complicated_match(cond: bool, items: (bool, bool, String)) -> i32 {
+    match items {
+        (false, a, s) | (a, false, s) if if cond { return 3 } else { a } => 1,
+        (true, b, t) | (false, b, t) => 2,
+    }
+}
+
+const CASES: &[(bool, bool, bool, i32)] = &[
+    (false, false, false, 2),
+    (false, false, true, 1),
+    (false, true, false, 1),
+    (false, true, true, 2),
+    (true, false, false, 3),
+    (true, false, true, 3),
+    (true, true, false, 3),
+    (true, true, true, 2),
+];
+
+fn main() {
+    for &(cond, items_1, items_2, result) in CASES {
+        assert_eq!(
+            complicated_match(cond, (items_1, items_2, String::new())),
+            result,
+        );
+    }
+}
+
+// END RUST SOURCE
+// START rustc.complicated_match.SimplifyCfg-initial.after.mir
+// let mut _0: i32;
+// let mut _3: &bool;                   // Temp for fake borrow of `items.0`
+// let mut _4: &bool;                   // Temp for fake borrow of `items.1`
+// let _5: bool;                    // `a` in arm
+// let _6: &bool;                   // `a` in guard
+// let _7: std::string::String;     // `s` in arm
+// let _8: &std::string::String;    // `s` in guard
+// let mut _9: bool;                    // `if cond { return 3 } else { a }`
+// let mut _10: bool;                   // `cond`
+// let mut _11: !;                      // `return 3`
+// let mut _12: bool;                   // `if cond { return 3 } else { a }`
+// let mut _13: bool;                   // `cond`
+// let mut _14: !;                      // `return 3`
+// let _15: bool;                   // `b`
+// let _16: std::string::String;    // `t`
+// scope 1 {
+// }
+// scope 2 {
+// }
+// bb0: {
+//     FakeRead(ForMatchedPlace, _2);
+//     switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb7];
+// }
+// bb1 (cleanup): {
+//     resume;
+// }
+// bb2: {
+//     falseEdges -> [real: bb10, imaginary: bb3];
+// }
+// bb3: {
+//     falseEdges -> [real: bb21, imaginary: bb4];
+// }
+// bb4: {
+//     falseEdges -> [real: bb31, imaginary: bb5];
+// }
+// bb5: {
+//     falseEdges -> [real: bb32, imaginary: bb6];
+// }
+// bb6: {
+//     unreachable;
+// }
+// bb7: {
+//     switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb8];
+// }
+// bb8: {
+//     switchInt((_2.0: bool)) -> [false: bb5, otherwise: bb4];
+// }
+// bb9: {                               // arm 1
+//     _0 = const 1i32;
+//     drop(_7) -> [return: bb29, unwind: bb16];
+// }
+// bb10: {                              // guard - first time
+//     StorageLive(_6);
+//     _6 = &(_2.1: bool);
+//     StorageLive(_8);
+//     _8 = &(_2.2: std::string::String);
+//     _3 = &shallow (_2.0: bool);
+//     _4 = &shallow (_2.1: bool);
+//     StorageLive(_9);
+//     StorageLive(_10);
+//     _10 = _1;
+//     FakeRead(ForMatchedPlace, _10);
+//     switchInt(_10) -> [false: bb12, otherwise: bb11];
+// }
+// bb11: {
+//     falseEdges -> [real: bb14, imaginary: bb12];
+// }
+// bb12: {
+//     falseEdges -> [real: bb18, imaginary: bb13];
+// }
+// bb13: {
+//     unreachable;
+// }
+// bb14: {                              // `return 3` - first time
+//     _0 = const 3i32;
+//     StorageDead(_10);
+//     StorageDead(_9);
+//     StorageDead(_8);
+//     StorageDead(_6);
+//     goto -> bb17;
+// }
+// bb15: {
+//     return;
+// }
+// bb16 (cleanup): {
+//     drop(_2) -> bb1;
+// }
+// bb17: {
+//     drop(_2) -> [return: bb15, unwind: bb1];
+// }
+// bb18: {                              // `else` block - first time
+//     _9 = (*_6);
+//     StorageDead(_10);
+//     FakeRead(ForMatchGuard, _3);
+//     FakeRead(ForMatchGuard, _4);
+//     FakeRead(ForGuardBinding, _6);
+//     FakeRead(ForGuardBinding, _8);
+//     switchInt(move _9) -> [false: bb20, otherwise: bb19];
+// }
+// bb19: {
+//     StorageDead(_9);
+//     StorageLive(_5);
+//     _5 = (_2.1: bool);
+//     StorageLive(_7);
+//     _7 = move (_2.2: std::string::String);
+//     goto -> bb9;
+// }
+// bb20: {                              // guard otherwise case - first time
+//     StorageDead(_9);
+//     StorageDead(_8);
+//     StorageDead(_6);
+//     falseEdges -> [real: bb7, imaginary: bb3];
+// }
+// bb21: {                              // guard - second time
+//     StorageLive(_6);
+//     _6 = &(_2.0: bool);
+//     StorageLive(_8);
+//     _8 = &(_2.2: std::string::String);
+//     _3 = &shallow (_2.0: bool);
+//     _4 = &shallow (_2.1: bool);
+//     StorageLive(_12);
+//     StorageLive(_13);
+//     _13 = _1;
+//     FakeRead(ForMatchedPlace, _13);
+//     switchInt(_13) -> [false: bb23, otherwise: bb22];
+// }
+// bb22: {
+//     falseEdges -> [real: bb25, imaginary: bb23];
+// }
+// bb23: {
+//     falseEdges -> [real: bb26, imaginary: bb24];
+// }
+// bb24: {
+//     unreachable;
+// }
+// bb25: {                              // `return 3` - second time
+//     _0 = const 3i32;
+//     StorageDead(_13);
+//     StorageDead(_12);
+//     StorageDead(_8);
+//     StorageDead(_6);
+//     goto -> bb17;
+// }
+// bb26: {                              // `else` block - second time
+//     _12 = (*_6);
+//     StorageDead(_13);
+//     FakeRead(ForMatchGuard, _3);
+//     FakeRead(ForMatchGuard, _4);
+//     FakeRead(ForGuardBinding, _6);
+//     FakeRead(ForGuardBinding, _8);
+//     switchInt(move _12) -> [false: bb28, otherwise: bb27];
+// }
+// bb27: {                              // Guard otherwise case - second time
+//     StorageDead(_12);
+//     StorageLive(_5);
+//     _5 = (_2.0: bool);
+//     StorageLive(_7);
+//     _7 = move (_2.2: std::string::String);
+//     goto -> bb9;
+// }
+// bb28: {                              // rest of arm 1
+//     StorageDead(_12);
+//     StorageDead(_8);
+//     StorageDead(_6);
+//     falseEdges -> [real: bb8, imaginary: bb4];
+// }
+// bb29: {
+//     StorageDead(_7);
+//     StorageDead(_5);
+//     StorageDead(_8);
+//     StorageDead(_6);
+//     goto -> bb34;
+// }
+// bb30: {                              // arm 2
+//     _0 = const 2i32;
+//     drop(_16) -> [return: bb33, unwind: bb16];
+// }
+// bb31: {                              // bindings for arm 2 - first pattern
+//     StorageLive(_15);
+//     _15 = (_2.1: bool);
+//     StorageLive(_16);
+//     _16 = move (_2.2: std::string::String);
+//     goto -> bb30;
+// }
+// bb32: {                              // bindings for arm 2 - first pattern
+//     StorageLive(_15);
+//     _15 = (_2.1: bool);
+//     StorageLive(_16);
+//     _16 = move (_2.2: std::string::String);
+//     goto -> bb30;
+// }
+// bb33: {                              // rest of arm 2
+//     StorageDead(_16);
+//     StorageDead(_15);
+//     goto -> bb34;
+// }
+// bb34: {                              // end of match
+//     drop(_2) -> [return: bb15, unwind: bb1];
+// }
+// END rustc.complicated_match.SimplifyCfg-initial.after.mir
+// START rustc.complicated_match.ElaborateDrops.after.mir
+// let _16: std::string::String;      // No drop flags, which would come after this.
+// scope 1 {
+// END rustc.complicated_match.ElaborateDrops.after.mir
index 7ac36a22274f33897dfe8e2a827b7c97f373a010..6979924c8cd9083b4986665d81abb6c2df17e97a 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(_2);
+//      StorageDead(_1);
+//      _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(_2);
+//      StorageDead(_1);
+//      _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(_2);
+//      StorageDead(_1);
+//      _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 7e8c58e64c28d60d2689682e094f927291c0da75..da73cc96348f059e4c15e29ce839204b2b824ed4 100644 (file)
@@ -18,15 +18,13 @@ fn drop(&mut self) {}
 // START rustc.main.EraseRegions.before.mir
 // fn main() -> () {
 //     let mut _0: ();
+//     let mut _1: Packed;
 //     let mut _2: Aligned;
 //     let mut _3: Droppy;
 //     let mut _4: Aligned;
 //     let mut _5: Droppy;
 //     let mut _6: Aligned;
 //     scope 1 {
-//         let mut _1: Packed;
-//     }
-//     scope 2 {
 //     }
 //
 //     bb0: {
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 10f00cf8b0c32869aa294faec4e8f9af3e79c81c..2ed34ecfad2c6317f8f4b9a07db528b963ff26e9 100644 (file)
@@ -182,8 +182,8 @@ fn main() {
 //        _2 = Foo { tup: const "hi", data: move _3 };
 //        _1 = &_2;
 //        _0 = &(*_1);
-//        StorageDead(_1);
 //        StorageDead(_5);
+//        StorageDead(_1);
 //        return;
 //    }
 //}
diff --git a/src/test/ui/lint/lint-match-arms.rs b/src/test/ui/lint/lint-match-arms.rs
new file mode 100644 (file)
index 0000000..2c471a6
--- /dev/null
@@ -0,0 +1,18 @@
+fn deny_on_arm() {
+    match 0 {
+        #[deny(unused_variables)]
+        //~^ NOTE lint level defined here
+        y => (),
+        //~^ ERROR unused variable
+    }
+}
+
+#[deny(unused_variables)]
+fn allow_on_arm() {
+    match 0 {
+        #[allow(unused_variables)]
+        y => (), // OK
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/lint-match-arms.stderr b/src/test/ui/lint/lint-match-arms.stderr
new file mode 100644 (file)
index 0000000..e4e3ada
--- /dev/null
@@ -0,0 +1,14 @@
+error: unused variable: `y`
+  --> $DIR/lint-match-arms.rs:5:9
+   |
+LL |         y => (),
+   |         ^ help: consider prefixing with an underscore: `_y`
+   |
+note: lint level defined here
+  --> $DIR/lint-match-arms.rs:3:16
+   |
+LL |         #[deny(unused_variables)]
+   |                ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
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)]
    |        ^^^^^^^^^^