diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 2e54165be1f1b..ef0d4be268eaf 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -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 diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 38d6d710868c0..517c99f99efea 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -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 { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index dd0d13d8f5a6a..05aa7690663e1 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1313,6 +1313,7 @@ impl<'a> LoweringContext<'a> { 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 { @@ -1320,6 +1321,7 @@ impl<'a> LoweringContext<'a> { _ => None, }, body: P(self.lower_expr(&arm.body)), + span: arm.span, } } @@ -5023,9 +5025,11 @@ impl<'a> LoweringContext<'a> { fn arm(&mut self, pats: hir::HirVec>, expr: P) -> hir::Arm { hir::Arm { + hir_id: self.next_id(), attrs: hir_vec![], pats, guard: None, + span: expr.span, body: expr, } } diff --git a/src/librustc/hir/map/collector.rs b/src/librustc/hir/map/collector.rs index a1cf338bf12ea..accf8adcf9bdb 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc/hir/map/collector.rs @@ -430,6 +430,16 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + 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)); diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index b8ee98551a20e..1da23dd8919d4 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -373,6 +373,7 @@ impl<'hir> Map<'hir> { Node::Pat(_) | Node::Binding(_) | Node::Local(_) | + Node::Arm(_) | Node::Lifetime(_) | Node::Visibility(_) | Node::Block(_) | @@ -1000,6 +1001,7 @@ impl<'hir> Map<'hir> { 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(¶m.attrs[..]), // Unit/tuple structs/variants take the attributes straight from // the struct/variant definition. @@ -1073,6 +1075,7 @@ impl<'hir> Map<'hir> { 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 @@ impl<'a> print::State<'a> { 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) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index f407be4e87b82..43b9487dac582 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1228,6 +1228,9 @@ pub struct Local { /// ` (if ) => `. #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] pub struct Arm { + #[stable_hasher(ignore)] + pub hir_id: HirId, + pub span: Span, pub attrs: HirVec, /// Multiple patterns can be combined with `|` pub pats: HirVec>, @@ -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), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 8a9028e544391..ef9fee5cab694 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -1862,7 +1862,7 @@ impl<'a> State<'a> { 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() { diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 68b65f9b4a1cc..3682552d22d75 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -849,6 +849,12 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'a, 'tcx> { }) } + 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); diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 8ef4e9ac8f45f..4f70d72cbffa8 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -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!(ASSERT_SCOPE_DATA: mem::size_of::() == 4); @@ -814,6 +814,16 @@ fn resolve_block<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, blk: } fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &'tcx hir::Arm) { + let prev_cx = visitor.cx; + + visitor.enter_scope( + Scope { + id: arm.hir_id.local_id, + data: ScopeData::Node, + } + ); + visitor.cx.var_parent = visitor.cx.parent; + visitor.terminating_scopes.insert(arm.body.hir_id.local_id); if let Some(hir::Guard::If(ref expr)) = arm.guard { @@ -821,6 +831,8 @@ fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: & } intravisit::walk_arm(visitor, arm); + + visitor.cx = prev_cx; } fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &'tcx hir::Pat) { @@ -893,10 +905,6 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr: terminating(body.hir_id.local_id); } - hir::ExprKind::Match(..) => { - visitor.cx.var_parent = visitor.cx.parent; - } - hir::ExprKind::DropTemps(ref expr) => { // `DropTemps(expr)` does not denote a conditional scope. // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`. diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 7469aceee3a9e..b5bab1585342a 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -23,8 +23,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }) } - // 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; } } diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index e354a2ee8160b..ed80cb1a16369 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -57,7 +57,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { { 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) }); } diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index 5f444d4ceeb88..a956eacb0699f 100644 --- a/src/librustc_mir/build/expr/as_place.rs +++ b/src/librustc_mir/build/expr/as_place.rs @@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index fbc4835a6557b..a0b504a99de9a 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -58,7 +58,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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) }) } diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index cba771f27065d..cffd8fb2892f5 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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(), + }, ); } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 15795a64e3b7d..7bdfdf0b0895f 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -46,7 +46,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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) }) } diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index b58914b017fd4..ac690f89264bf 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -29,7 +29,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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) }) } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 73a3c3be3aa57..8c2ef082c339f 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -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 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 @@ 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 }, ); @@ -489,33 +492,20 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { &mut self, mut visibility_scope: Option, scope_span: Span, - lint_level: LintLevel, pattern: &Pattern<'tcx>, has_guard: ArmHasGuard, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { - 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 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { span, untested_candidates, join_block, - &mut None, + fake_borrows, ) } @@ -1328,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); @@ -1510,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 } }); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 16ab233bd2e36..8cd4ac0ad3ade 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -702,13 +702,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 @@ -945,10 +945,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { 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)); } } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 4aa463b37ab77..34df40ae180af 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -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 @@ -279,13 +284,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn in_opt_scope(&mut self, opt_scope: Option<(region::Scope, SourceInfo)>, - mut block: BasicBlock, f: F) -> BlockAnd where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd { - 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)); @@ -299,12 +304,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn in_scope(&mut self, region_scope: (region::Scope, SourceInfo), lint_level: LintLevel, - mut block: BasicBlock, f: F) -> BlockAnd where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd { - 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 { @@ -330,6 +334,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } 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; @@ -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. diff --git a/src/librustc_mir/dataflow/impls/mod.rs b/src/librustc_mir/dataflow/impls/mod.rs index 4dcfb3f1a7fc3..03d55b84f32e2 100644 --- a/src/librustc_mir/dataflow/impls/mod.rs +++ b/src/librustc_mir/dataflow/impls/mod.rs @@ -11,8 +11,7 @@ use super::MoveDataParamEnv; 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 @@ impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tc 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]); } _ => {} } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index f58055ea28419..ca457b0ac7c72 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -873,8 +873,12 @@ fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) _ => None, }, body: arm.body.to_ref(), - // BUG: fix this - lint_level: LintLevel::Inherited, + lint_level: LintLevel::Explicit(arm.hir_id), + scope: region::Scope { + id: arm.hir_id.local_id, + data: region::ScopeData::Node + }, + span: arm.span, } } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index d4f139e103a64..8e19913f4df26 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -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>, pub body: ExprRef<'tcx>, pub lint_level: LintLevel, + pub scope: region::Scope, + pub span: Span, } #[derive(Clone, Debug)] diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs index c74314ce0c4b5..0088c97679c66 100644 --- a/src/librustc_passes/hir_stats.rs +++ b/src/librustc_passes/hir_stats.rs @@ -149,7 +149,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } 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) } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index a69f639e8941f..e4b431e6e68f1 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -540,7 +540,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); 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 { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d12240655e628..14c77e589a6a1 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -912,6 +912,7 @@ pub struct Arm { pub pats: Vec>, pub guard: Option, pub body: P, + pub span: Span, } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index d24106f697e19..cb967a76822c9 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -890,12 +890,13 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.pat_tuple_struct(span, path, vec![pat]) } - fn arm(&self, _span: Span, pats: Vec>, expr: P) -> ast::Arm { + fn arm(&self, span: Span, pats: Vec>, expr: P) -> ast::Arm { ast::Arm { attrs: vec![], pats, guard: None, body: expr, + span, } } diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index f587e63e12b94..cb21014ec7646 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -392,11 +392,15 @@ pub fn noop_visit_use_tree(use_tree: &mut UseTree, vis: &mut T) { vis.visit_span(span); } -pub fn noop_visit_arm(Arm { attrs, pats, guard, body }: &mut Arm, vis: &mut T) { +pub fn noop_visit_arm( + 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(g: &mut Guard, vis: &mut T) { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2d6c8c5407583..1c897f1bda944 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4041,6 +4041,7 @@ impl<'a> Parser<'a> { 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(keywords::If) { Some(Guard::If(self.parse_expr()?)) @@ -4060,6 +4061,8 @@ impl<'a> Parser<'a> { 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)]) @@ -4103,6 +4106,7 @@ impl<'a> Parser<'a> { pats, guard, body: expr, + span: lo.to(hi), }) } diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs index 4a5a4e0677aa4..9f55d84964405 100644 --- a/src/test/mir-opt/box_expr.rs +++ b/src/test/mir-opt/box_expr.rs @@ -22,13 +22,11 @@ impl Drop for S { // END RUST SOURCE // START rustc.main.ElaborateDrops.before.mir // let mut _0: (); +// let _1: std::boxed::Box; // let mut _2: std::boxed::Box; // let mut _3: (); // let mut _4: std::boxed::Box; // scope 1 { -// let _1: std::boxed::Box; -// } -// scope 2 { // } // bb0: { // StorageLive(_1); diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs index 023440af0eb10..0b678be2ab319 100644 --- a/src/test/mir-opt/issue-41110.rs +++ b/src/test/mir-opt/issue-41110.rs @@ -29,27 +29,24 @@ impl S { // 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: { diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index f72258dd9b998..447f3a07a6a98 100644 --- a/src/test/mir-opt/issue-49232.rs +++ b/src/test/mir-opt/issue-49232.rs @@ -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 index 0000000000000..0f026b8a08dfa --- /dev/null +++ b/src/test/mir-opt/match-arm-scopes.rs @@ -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 diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 7ac36a22274f3..6979924c8cd90 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -45,13 +45,13 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb7]; +// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb6]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb8, imaginary: bb3]; //pre_binding1 +// falseEdges -> [real: bb7, imaginary: bb3]; //pre_binding1 // } // bb3: { // falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2 @@ -62,48 +62,56 @@ fn main() { // bb5: { // unreachable; // } -// bb6: { // to pre_binding2 -// falseEdges -> [real: bb3, imaginary: bb3]; -// } -// bb7: { +// bb6: { // unreachable; // } -// bb8: { // binding1 and guard +// bb7: { // binding1 and guard // StorageLive(_6); // _6 = &(((promoted[0]: std::option::Option) as Some).0: i32); // _4 = &shallow _2; // StorageLive(_7); -// _7 = const guard() -> [return: bb9, unwind: bb1]; +// _7 = const guard() -> [return: bb8, unwind: bb1]; // } -// bb9: { +// bb8: { // end of guard // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); -// switchInt(move _7) -> [false: bb6, otherwise: bb10]; +// switchInt(move _7) -> [false: bb10, otherwise: bb9]; // } -// bb10: { +// bb9: { // arm1 +// StorageDead(_7); // StorageLive(_5); // _5 = ((_2 as Some).0: i32); // StorageLive(_8); // _8 = _5; // _1 = (const 1i32, move _8); // StorageDead(_8); +// StorageDead(_5); +// StorageDead(_6); // goto -> bb13; // } -// bb11: { +// bb10: { // to pre_binding2 +// StorageDead(_7); +// StorageDead(_6); +// falseEdges -> [real: bb3, imaginary: bb3]; +// } +// bb11: { // arm2 // StorageLive(_9); // _9 = ((_2 as Some).0: i32); // StorageLive(_10); // _10 = _9; // _1 = (const 2i32, move _10); // StorageDead(_10); +// StorageDead(_9); // goto -> bb13; // } -// bb12: { +// bb12: { // arm3 // _1 = (const 3i32, const 3i32); // goto -> bb13; // } // bb13: { -// ... +// StorageDead(_2); +// StorageDead(_1); +// _0 = (); // return; // } // END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir @@ -114,13 +122,13 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb7]; +// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb6]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb8, imaginary: bb3]; +// falseEdges -> [real: bb7, imaginary: bb3]; // } // bb3: { // falseEdges -> [real: bb11, imaginary: bb4]; @@ -131,33 +139,38 @@ fn main() { // bb5: { // unreachable; // } -// bb6: { // to pre_binding3 (can skip 2 since this is `Some`) -// falseEdges -> [real: bb4, imaginary: bb3]; -// } -// bb7: { +// bb6: { // unreachable; // } -// bb8: { // binding1 and guard +// bb7: { // binding1 and guard // StorageLive(_6); // _6 = &((_2 as Some).0: i32); // _4 = &shallow _2; // StorageLive(_7); -// _7 = const guard() -> [return: bb9, unwind: bb1]; +// _7 = const guard() -> [return: bb8, unwind: bb1]; // } -// bb9: { // end of guard +// bb8: { // end of guard // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); -// switchInt(move _7) -> [false: bb6, otherwise: bb10]; +// switchInt(move _7) -> [false: bb10, otherwise: bb9]; // } -// bb10: { // arm1 +// bb9: { // arm1 +// StorageDead(_7); // StorageLive(_5); // _5 = ((_2 as Some).0: i32); // StorageLive(_8); // _8 = _5; // _1 = (const 1i32, move _8); // StorageDead(_8); +// StorageDead(_5); +// StorageDead(_6); // goto -> bb13; // } +// bb10: { // to pre_binding3 (can skip 2 since this is `Some`) +// StorageDead(_7); +// StorageDead(_6); +// falseEdges -> [real: bb4, imaginary: bb3]; +// } // bb11: { // arm2 // _1 = (const 3i32, const 3i32); // goto -> bb13; @@ -169,16 +182,19 @@ fn main() { // _10 = _9; // _1 = (const 2i32, move _10); // StorageDead(_10); +// StorageDead(_9); // goto -> bb13; // } // bb13: { -// ... +// StorageDead(_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::::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 diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index a5317f98ef188..2ef9520c12c63 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -20,10 +20,10 @@ fn main() { // START rustc.main.SimplifyCfg-initial.after.mir // bb0: { // ... -// switchInt(move _4) -> [false: bb7, otherwise: bb8]; +// switchInt(move _4) -> [false: bb6, otherwise: bb7]; // } // bb1: { -// falseEdges -> [real: bb12, imaginary: bb2]; +// falseEdges -> [real: bb10, imaginary: bb2]; // } // bb2: { // falseEdges -> [real: bb13, imaginary: bb3]; @@ -38,33 +38,35 @@ fn main() { // unreachable; // } // bb6: { -// falseEdges -> [real: bb4, imaginary: bb2]; +// _6 = Le(const 10i32, _1); +// switchInt(move _6) -> [false: bb8, otherwise: bb9]; // } // bb7: { -// _6 = Le(const 10i32, _1); -// switchInt(move _6) -> [false: bb9, otherwise: bb10]; +// _5 = Lt(_1, const 10i32); +// switchInt(move _5) -> [false: bb6, otherwise: bb1]; // } // bb8: { -// _5 = Lt(_1, const 10i32); -// switchInt(move _5) -> [false: bb7, otherwise: bb1]; +// switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; // } // bb9: { -// switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; +// _7 = Le(_1, const 20i32); +// switchInt(move _7) -> [false: bb8, otherwise: bb2]; // } // bb10: { -// _7 = Le(_1, const 20i32); -// switchInt(move _7) -> [false: bb9, otherwise: bb2]; +// _8 = &shallow _1; +// StorageLive(_9); +// _9 = _2; +// FakeRead(ForMatchGuard, _8); +// switchInt(move _9) -> [false: bb12, otherwise: bb11]; // } // bb11: { +// StorageDead(_9); // _3 = const 0i32; // goto -> bb16; // } // bb12: { -// _8 = &shallow _1; -// StorageLive(_9); -// _9 = _2; -// FakeRead(ForMatchGuard, _8); -// switchInt(move _9) -> [false: bb6, otherwise: bb11]; +// StorageDead(_9); +// falseEdges -> [real: bb4, imaginary: bb2]; // } // bb13: { // _3 = const 1i32; @@ -79,7 +81,6 @@ fn main() { // goto -> bb16; // } // bb16: { -// StorageDead(_9); // _0 = (); // StorageDead(_2); // StorageDead(_1); diff --git a/src/test/mir-opt/packed-struct-drop-aligned.rs b/src/test/mir-opt/packed-struct-drop-aligned.rs index 7e8c58e64c28d..da73cc96348f0 100644 --- a/src/test/mir-opt/packed-struct-drop-aligned.rs +++ b/src/test/mir-opt/packed-struct-drop-aligned.rs @@ -18,15 +18,13 @@ impl Drop for Droppy { // 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: { diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index 144348450a91b..12de1e8a51c4b 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -21,10 +21,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; @@ -33,16 +33,9 @@ fn main() { // unreachable; // } // bb4: { -// goto -> bb2; -// } -// bb5: { // switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb6: { -// _0 = const 0i32; -// goto -> bb9; -// } -// bb7: { +// bb5: { // _4 = &shallow _1; // _5 = &shallow ((_1 as Some).0: &' &' i32); // _6 = &shallow (*((_1 as Some).0: &' &' i32)); @@ -53,14 +46,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): { @@ -72,10 +73,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; @@ -84,16 +85,9 @@ fn main() { // unreachable; // } // bb4: { -// goto -> bb2; -// } -// bb5: { // switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; // } -// bb6: { -// _0 = const 0i32; -// goto -> bb9; -// } -// bb7: { +// bb5: { // nop; // nop; // nop; @@ -104,14 +98,22 @@ fn main() { // nop; // nop; // nop; -// switchInt(move _8) -> [false: bb4, otherwise: bb6]; +// switchInt(move _8) -> [false: bb7, otherwise: bb6]; +// } +// bb6: { +// StorageDead(_8); +// _0 = const 0i32; +// goto -> bb9; +// } +// bb7: { +// StorageDead(_8); +// goto -> bb2; // } // bb8: { // _0 = const 1i32; // goto -> bb9; // } // bb9: { -// StorageDead(_8); // return; // } // bb10 (cleanup): { diff --git a/src/test/mir-opt/storage_live_dead_in_statics.rs b/src/test/mir-opt/storage_live_dead_in_statics.rs index 10f00cf8b0c32..2ed34ecfad2c6 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.rs +++ b/src/test/mir-opt/storage_live_dead_in_statics.rs @@ -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 index 0000000000000..2c471a61054b2 --- /dev/null +++ b/src/test/ui/lint/lint-match-arms.rs @@ -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 index 0000000000000..e4e3adab0a9b2 --- /dev/null +++ b/src/test/ui/lint/lint-match-arms.stderr @@ -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 + diff --git a/src/test/ui/lint/lint-unused-mut-variables.rs b/src/test/ui/lint/lint-unused-mut-variables.rs index da0236ac45070..78609a6e24b5e 100644 --- a/src/test/ui/lint/lint-unused-mut-variables.rs +++ b/src/test/ui/lint/lint-unused-mut-variables.rs @@ -105,6 +105,14 @@ fn 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; } diff --git a/src/test/ui/lint/lint-unused-mut-variables.stderr b/src/test/ui/lint/lint-unused-mut-variables.stderr index e41d8f8ac7408..1a175c9683ec7 100644 --- a/src/test/ui/lint/lint-unused-mut-variables.stderr +++ b/src/test/ui/lint/lint-unused-mut-variables.stderr @@ -133,7 +133,7 @@ LL | fn mut_ref_arg(mut arg : &mut [u8]) -> &mut [u8] { | help: remove this `mut` error: variable does not need to be mutable - --> $DIR/lint-unused-mut-variables.rs:130:9 + --> $DIR/lint-unused-mut-variables.rs:138:9 | LL | let mut b = vec![2]; | ----^ @@ -141,7 +141,7 @@ LL | let mut b = vec![2]; | help: remove this `mut` | note: lint level defined here - --> $DIR/lint-unused-mut-variables.rs:126:8 + --> $DIR/lint-unused-mut-variables.rs:134:8 | LL | #[deny(unused_mut)] | ^^^^^^^^^^