From e3319e7d1678b011de6af5b99caa4343ce8c4286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 27 Sep 2020 00:00:00 +0000 Subject: [PATCH 1/6] liveness: Move body_owner field from IrMaps to Liveness The Liveness struct is the only user of body_owner field. Move the field there. --- compiler/rustc_passes/src/liveness.rs | 30 +++++++++++---------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 9eac2f79e412c..565af3268593e 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -168,10 +168,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module( - module_def_id, - &mut IrMaps::new(tcx, module_def_id).as_deep_visitor(), - ); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx).as_deep_visitor()); } pub fn provide(providers: &mut Providers) { @@ -227,7 +224,6 @@ enum VarKind { struct IrMaps<'tcx> { tcx: TyCtxt<'tcx>, - body_owner: LocalDefId, live_node_map: HirIdMap, variable_map: HirIdMap, capture_info_map: HirIdMap>>, @@ -236,10 +232,9 @@ struct IrMaps<'tcx> { } impl IrMaps<'tcx> { - fn new(tcx: TyCtxt<'tcx>, body_owner: LocalDefId) -> IrMaps<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> IrMaps<'tcx> { IrMaps { tcx, - body_owner, live_node_map: HirIdMap::default(), variable_map: HirIdMap::default(), capture_info_map: Default::default(), @@ -316,7 +311,7 @@ fn visit_fn<'tcx>( // swap in a new set of IR maps for this function body: let def_id = ir.tcx.hir().local_def_id(id); - let mut fn_maps = IrMaps::new(ir.tcx, def_id); + let mut fn_maps = IrMaps::new(ir.tcx); // Don't run unused pass for #[derive()] if let FnKind::Method(..) = fk { @@ -448,10 +443,7 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { })); } ir.set_captures(expr.hir_id, call_caps); - let old_body_owner = ir.body_owner; - ir.body_owner = closure_def_id; intravisit::walk_expr(ir, expr); - ir.body_owner = old_body_owner; } // live nodes required for interesting control flow: @@ -605,6 +597,7 @@ const ACC_USE: u32 = 4; struct Liveness<'a, 'tcx> { ir: &'a mut IrMaps<'tcx>, + body_owner: LocalDefId, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, successors: IndexVec, @@ -626,9 +619,9 @@ struct Liveness<'a, 'tcx> { } impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> { - let typeck_results = ir.tcx.typeck(def_id); - let param_env = ir.tcx.param_env(def_id); + fn new(ir: &'a mut IrMaps<'tcx>, body_owner: LocalDefId) -> Liveness<'a, 'tcx> { + let typeck_results = ir.tcx.typeck(body_owner); + let param_env = ir.tcx.param_env(body_owner); let closure_ln = ir.add_live_node(ClosureNode); let exit_ln = ir.add_live_node(ExitNode); @@ -638,6 +631,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { Liveness { ir, + body_owner, typeck_results, param_env, successors: IndexVec::from_elem_n(INVALID_NODE, num_live_nodes), @@ -893,12 +887,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) { + if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.body_owner) { // Mark upvars captured by reference as used after closure exits. for (&var_hir_id, upvar) in upvars.iter().rev() { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: self.ir.body_owner, + closure_expr_id: self.body_owner, }; match self.typeck_results.upvar_capture(upvar_id) { ty::UpvarCapture::ByRef(_) => { @@ -1565,7 +1559,7 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let upvars = match self.ir.tcx.upvars_mentioned(self.ir.body_owner) { + let upvars = match self.ir.tcx.upvars_mentioned(self.body_owner) { None => return, Some(upvars) => upvars, }; @@ -1573,7 +1567,7 @@ impl<'tcx> Liveness<'_, 'tcx> { let var = self.variable(var_hir_id, upvar.span); let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: self.ir.body_owner, + closure_expr_id: self.body_owner, }; match self.typeck_results.upvar_capture(upvar_id) { ty::UpvarCapture::ByValue(_) => {} From e0db21dc7e4c8ab897ff07dcf0cce293db6ff4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 27 Sep 2020 00:00:00 +0000 Subject: [PATCH 2/6] liveness: Use upvars instead of FnKind to check for closures Avoiding FnKind will make it easier to run liveness analysis on all bodies in the future, not just fn-like things. --- compiler/rustc_passes/src/liveness.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 565af3268593e..2cc9075196b55 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -355,7 +355,7 @@ fn visit_fn<'tcx>( // compute liveness let mut lsets = Liveness::new(&mut fn_maps, def_id); - let entry_ln = lsets.compute(fk, &body, sp, id); + let entry_ln = lsets.compute(&body, sp, id); lsets.log_liveness(entry_ln, id); // check for various error conditions @@ -862,13 +862,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.rwu_table.assign_unpacked(idx, rwu); } - fn compute( - &mut self, - fk: FnKind<'_>, - body: &hir::Body<'_>, - span: Span, - id: hir::HirId, - ) -> LiveNode { + fn compute(&mut self, body: &hir::Body<'_>, span: Span, id: hir::HirId) -> LiveNode { debug!("compute: using id for body, {:?}", body.value); // # Liveness of captured variables @@ -887,7 +881,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.body_owner) { + let upvars = self.ir.tcx.upvars_mentioned(self.body_owner); + if let Some(upvars) = upvars { // Mark upvars captured by reference as used after closure exits. for (&var_hir_id, upvar) in upvars.iter().rev() { let upvar_id = ty::UpvarId { @@ -906,9 +901,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let succ = self.propagate_through_expr(&body.value, self.exit_ln); - match fk { - FnKind::Method(..) | FnKind::ItemFn(..) => return succ, - FnKind::Closure(..) => {} + if upvars.is_none() { + // Either not a closure, or closure without any captured variables. + // No need to determine liveness of captured variables, since there + // are none. + return succ; } let ty = self.typeck_results.node_type(id); @@ -920,7 +917,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { }, ty::Generator(..) => return succ, _ => { - span_bug!(span, "type of closure expr {:?} is not a closure {:?}", id, ty,); + span_bug!(span, "{} has upvars so it should have a closure type: {:?}", id, ty); } }; From b629ffd96bd4c882322833f6cde8c41b115894f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 27 Sep 2020 00:00:00 +0000 Subject: [PATCH 3/6] liveness: Use visit_param to add variables corresponding to params --- compiler/rustc_passes/src/liveness.rs | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 2cc9075196b55..f399463e5ad45 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -165,6 +165,9 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { visit_arm(self, a); } + fn visit_param(&mut self, p: &'tcx hir::Param<'tcx>) { + visit_param(self, p); + } } fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { @@ -334,21 +337,6 @@ fn visit_fn<'tcx>( } } - for param in body.params { - let is_shorthand = match param.pat.kind { - rustc_hir::PatKind::Struct(..) => true, - _ => false, - }; - param.pat.each_binding(|_bm, hir_id, _x, ident| { - let var = if is_shorthand { - Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true }) - } else { - Param(hir_id, ident.name) - }; - fn_maps.add_variable(var); - }) - } - // gather up the various local variables, significant expressions, // and so forth: intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id); @@ -415,6 +403,22 @@ fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm<'tcx>) { intravisit::walk_arm(ir, arm); } +fn visit_param<'tcx>(ir: &mut IrMaps<'tcx>, param: &'tcx hir::Param<'tcx>) { + let is_shorthand = match param.pat.kind { + rustc_hir::PatKind::Struct(..) => true, + _ => false, + }; + param.pat.each_binding(|_bm, hir_id, _x, ident| { + let var = if is_shorthand { + Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true }) + } else { + Param(hir_id, ident.name) + }; + ir.add_variable(var); + }); + intravisit::walk_param(ir, param); +} + fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { match expr.kind { // live nodes required for uses or definitions of variables: From 536b51aca016b4744596f16a2e502b76e9490664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 27 Sep 2020 00:00:00 +0000 Subject: [PATCH 4/6] liveness: Store upvars_mentioned inside Liveness struct --- compiler/rustc_passes/src/liveness.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index f399463e5ad45..501bdfbbfff63 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -604,6 +604,7 @@ struct Liveness<'a, 'tcx> { body_owner: LocalDefId, typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, + upvars: Option<&'tcx FxIndexMap>, successors: IndexVec, rwu_table: RWUTable, @@ -626,6 +627,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn new(ir: &'a mut IrMaps<'tcx>, body_owner: LocalDefId) -> Liveness<'a, 'tcx> { let typeck_results = ir.tcx.typeck(body_owner); let param_env = ir.tcx.param_env(body_owner); + let upvars = ir.tcx.upvars_mentioned(body_owner); let closure_ln = ir.add_live_node(ClosureNode); let exit_ln = ir.add_live_node(ExitNode); @@ -638,6 +640,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { body_owner, typeck_results, param_env, + upvars, successors: IndexVec::from_elem_n(INVALID_NODE, num_live_nodes), rwu_table: RWUTable::new(num_live_nodes * num_vars), closure_ln, @@ -885,8 +888,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - let upvars = self.ir.tcx.upvars_mentioned(self.body_owner); - if let Some(upvars) = upvars { + if let Some(upvars) = self.upvars { // Mark upvars captured by reference as used after closure exits. for (&var_hir_id, upvar) in upvars.iter().rev() { let upvar_id = ty::UpvarId { @@ -905,7 +907,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let succ = self.propagate_through_expr(&body.value, self.exit_ln); - if upvars.is_none() { + if self.upvars.is_none() { // Either not a closure, or closure without any captured variables. // No need to determine liveness of captured variables, since there // are none. @@ -1560,7 +1562,7 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let upvars = match self.ir.tcx.upvars_mentioned(self.body_owner) { + let upvars = match self.upvars { None => return, Some(upvars) => upvars, }; From 33dde94d33f4200f1931d3ad0a28d798f807f27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 27 Sep 2020 00:00:00 +0000 Subject: [PATCH 5/6] liveness: Inline visitor implementation for IrMaps --- compiler/rustc_passes/src/liveness.rs | 374 ++++++++++++-------------- 1 file changed, 175 insertions(+), 199 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 501bdfbbfff63..eb2f40a82d1f1 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -138,38 +138,6 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { } } -impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.tcx.hir()) - } - - fn visit_fn( - &mut self, - fk: FnKind<'tcx>, - fd: &'tcx hir::FnDecl<'tcx>, - b: hir::BodyId, - s: Span, - id: HirId, - ) { - visit_fn(self, fk, fd, b, s, id); - } - - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - visit_local(self, l); - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - visit_expr(self, ex); - } - fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { - visit_arm(self, a); - } - fn visit_param(&mut self, p: &'tcx hir::Param<'tcx>) { - visit_param(self, p); - } -} - fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx).as_deep_visitor()); } @@ -300,196 +268,204 @@ impl IrMaps<'tcx> { fn set_captures(&mut self, hir_id: HirId, cs: Vec) { self.capture_info_map.insert(hir_id, Rc::new(cs)); } -} -fn visit_fn<'tcx>( - ir: &mut IrMaps<'tcx>, - fk: FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - sp: Span, - id: hir::HirId, -) { - debug!("visit_fn {:?}", id); - - // swap in a new set of IR maps for this function body: - let def_id = ir.tcx.hir().local_def_id(id); - let mut fn_maps = IrMaps::new(ir.tcx); - - // Don't run unused pass for #[derive()] - if let FnKind::Method(..) = fk { - let parent = ir.tcx.hir().get_parent_item(id); - if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) { - if i.attrs.iter().any(|a| ir.tcx.sess.check_name(a, sym::automatically_derived)) { - return; + fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) { + // For struct patterns, take note of which fields used shorthand + // (`x` rather than `x: x`). + let mut shorthand_field_ids = HirIdSet::default(); + let mut pats = VecDeque::new(); + pats.push_back(pat); + while let Some(pat) = pats.pop_front() { + use rustc_hir::PatKind::*; + match &pat.kind { + Binding(.., inner_pat) => { + pats.extend(inner_pat.iter()); + } + Struct(_, fields, _) => { + let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id); + shorthand_field_ids.extend(ids); + } + Ref(inner_pat, _) | Box(inner_pat) => { + pats.push_back(inner_pat); + } + TupleStruct(_, inner_pats, _) | Tuple(inner_pats, _) | Or(inner_pats) => { + pats.extend(inner_pats.iter()); + } + Slice(pre_pats, inner_pat, post_pats) => { + pats.extend(pre_pats.iter()); + pats.extend(inner_pat.iter()); + pats.extend(post_pats.iter()); + } + _ => {} } } - } - debug!("creating fn_maps: {:p}", &fn_maps); + pat.each_binding(|_, hir_id, _, ident| { + self.add_live_node_for_node(hir_id, VarDefNode(ident.span)); + self.add_variable(Local(LocalInfo { + id: hir_id, + name: ident.name, + is_shorthand: shorthand_field_ids.contains(&hir_id), + })); + }); + } +} - let body = ir.tcx.hir().body(body_id); +impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { + type Map = Map<'tcx>; - if let Some(upvars) = ir.tcx.upvars_mentioned(def_id) { - for (&var_hir_id, _upvar) in upvars { - let var_name = ir.tcx.hir().name(var_hir_id); - fn_maps.add_variable(Upvar(var_hir_id, var_name)); - } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.tcx.hir()) } - // gather up the various local variables, significant expressions, - // and so forth: - intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id); + fn visit_fn( + &mut self, + fk: FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + sp: Span, + id: HirId, + ) { + debug!("visit_fn {:?}", id); + + // swap in a new set of IR maps for this function body: + let def_id = self.tcx.hir().local_def_id(id); + let mut fn_maps = IrMaps::new(self.tcx); + + // Don't run unused pass for #[derive()] + if let FnKind::Method(..) = fk { + let parent = self.tcx.hir().get_parent_item(id); + if let Some(Node::Item(i)) = self.tcx.hir().find(parent) { + if i.attrs.iter().any(|a| self.tcx.sess.check_name(a, sym::automatically_derived)) { + return; + } + } + } - // compute liveness - let mut lsets = Liveness::new(&mut fn_maps, def_id); - let entry_ln = lsets.compute(&body, sp, id); - lsets.log_liveness(entry_ln, id); + debug!("creating fn_maps: {:p}", &fn_maps); - // check for various error conditions - lsets.visit_body(body); - lsets.warn_about_unused_upvars(entry_ln); - lsets.warn_about_unused_args(body, entry_ln); -} + let body = self.tcx.hir().body(body_id); -fn add_from_pat(ir: &mut IrMaps<'_>, pat: &hir::Pat<'_>) { - // For struct patterns, take note of which fields used shorthand - // (`x` rather than `x: x`). - let mut shorthand_field_ids = HirIdSet::default(); - let mut pats = VecDeque::new(); - pats.push_back(pat); - while let Some(pat) = pats.pop_front() { - use rustc_hir::PatKind::*; - match &pat.kind { - Binding(.., inner_pat) => { - pats.extend(inner_pat.iter()); - } - Struct(_, fields, _) => { - let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id); - shorthand_field_ids.extend(ids); - } - Ref(inner_pat, _) | Box(inner_pat) => { - pats.push_back(inner_pat); + if let Some(upvars) = self.tcx.upvars_mentioned(def_id) { + for (&var_hir_id, _upvar) in upvars { + let var_name = self.tcx.hir().name(var_hir_id); + fn_maps.add_variable(Upvar(var_hir_id, var_name)); } - TupleStruct(_, inner_pats, _) | Tuple(inner_pats, _) | Or(inner_pats) => { - pats.extend(inner_pats.iter()); - } - Slice(pre_pats, inner_pat, post_pats) => { - pats.extend(pre_pats.iter()); - pats.extend(inner_pat.iter()); - pats.extend(post_pats.iter()); - } - _ => {} } - } - pat.each_binding(|_, hir_id, _, ident| { - ir.add_live_node_for_node(hir_id, VarDefNode(ident.span)); - ir.add_variable(Local(LocalInfo { - id: hir_id, - name: ident.name, - is_shorthand: shorthand_field_ids.contains(&hir_id), - })); - }); -} + // gather up the various local variables, significant expressions, + // and so forth: + intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id); -fn visit_local<'tcx>(ir: &mut IrMaps<'tcx>, local: &'tcx hir::Local<'tcx>) { - add_from_pat(ir, &local.pat); - intravisit::walk_local(ir, local); -} + // compute liveness + let mut lsets = Liveness::new(&mut fn_maps, def_id); + let entry_ln = lsets.compute(&body, sp, id); + lsets.log_liveness(entry_ln, id); -fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm<'tcx>) { - add_from_pat(ir, &arm.pat); - intravisit::walk_arm(ir, arm); -} + // check for various error conditions + lsets.visit_body(body); + lsets.warn_about_unused_upvars(entry_ln); + lsets.warn_about_unused_args(body, entry_ln); + } -fn visit_param<'tcx>(ir: &mut IrMaps<'tcx>, param: &'tcx hir::Param<'tcx>) { - let is_shorthand = match param.pat.kind { - rustc_hir::PatKind::Struct(..) => true, - _ => false, - }; - param.pat.each_binding(|_bm, hir_id, _x, ident| { - let var = if is_shorthand { - Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true }) - } else { - Param(hir_id, ident.name) + fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + self.add_from_pat(&local.pat); + intravisit::walk_local(self, local); + } + + fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { + self.add_from_pat(&arm.pat); + intravisit::walk_arm(self, arm); + } + + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { + let is_shorthand = match param.pat.kind { + rustc_hir::PatKind::Struct(..) => true, + _ => false, }; - ir.add_variable(var); - }); - intravisit::walk_param(ir, param); -} + param.pat.each_binding(|_bm, hir_id, _x, ident| { + let var = if is_shorthand { + Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true }) + } else { + Param(hir_id, ident.name) + }; + self.add_variable(var); + }); + intravisit::walk_param(self, param); + } -fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { - match expr.kind { - // live nodes required for uses or definitions of variables: - hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { - debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res); - if let Res::Local(_var_hir_id) = path.res { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + // live nodes required for uses or definitions of variables: + hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { + debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res); + if let Res::Local(_var_hir_id) = path.res { + self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); + } + intravisit::walk_expr(self, expr); } - intravisit::walk_expr(ir, expr); - } - hir::ExprKind::Closure(..) => { - // Interesting control flow (for loops can contain labeled - // breaks or continues) - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - - // Make a live_node for each captured variable, with the span - // being the location that the variable is used. This results - // in better error messages than just pointing at the closure - // construction site. - let mut call_caps = Vec::new(); - let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = ir.tcx.upvars_mentioned(closure_def_id) { - call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { - let upvar_ln = ir.add_live_node(UpvarNode(upvar.span)); - CaptureInfo { ln: upvar_ln, var_hid: var_id } - })); + hir::ExprKind::Closure(..) => { + // Interesting control flow (for loops can contain labeled + // breaks or continues) + self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); + + // Make a live_node for each captured variable, with the span + // being the location that the variable is used. This results + // in better error messages than just pointing at the closure + // construction site. + let mut call_caps = Vec::new(); + let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { + let upvar_ln = self.add_live_node(UpvarNode(upvar.span)); + CaptureInfo { ln: upvar_ln, var_hid: var_id } + })); + } + self.set_captures(expr.hir_id, call_caps); + intravisit::walk_expr(self, expr); } - ir.set_captures(expr.hir_id, call_caps); - intravisit::walk_expr(ir, expr); - } - // live nodes required for interesting control flow: - hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) => { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - intravisit::walk_expr(ir, expr); - } - hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - intravisit::walk_expr(ir, expr); - } + // live nodes required for interesting control flow: + hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) => { + self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); + intravisit::walk_expr(self, expr); + } + hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => { + self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); + intravisit::walk_expr(self, expr); + } - // otherwise, live nodes are not required: - hir::ExprKind::Index(..) - | hir::ExprKind::Field(..) - | hir::ExprKind::Array(..) - | hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) - | hir::ExprKind::Tup(..) - | hir::ExprKind::Binary(..) - | hir::ExprKind::AddrOf(..) - | hir::ExprKind::Cast(..) - | hir::ExprKind::DropTemps(..) - | hir::ExprKind::Unary(..) - | hir::ExprKind::Break(..) - | hir::ExprKind::Continue(_) - | hir::ExprKind::Lit(_) - | hir::ExprKind::Ret(..) - | hir::ExprKind::Block(..) - | hir::ExprKind::Assign(..) - | hir::ExprKind::AssignOp(..) - | hir::ExprKind::Struct(..) - | hir::ExprKind::Repeat(..) - | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::LlvmInlineAsm(..) - | hir::ExprKind::Box(..) - | hir::ExprKind::Yield(..) - | hir::ExprKind::Type(..) - | hir::ExprKind::Err - | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) - | hir::ExprKind::Path(hir::QPath::LangItem(..)) => { - intravisit::walk_expr(ir, expr); + // otherwise, live nodes are not required: + hir::ExprKind::Index(..) + | hir::ExprKind::Field(..) + | hir::ExprKind::Array(..) + | hir::ExprKind::Call(..) + | hir::ExprKind::MethodCall(..) + | hir::ExprKind::Tup(..) + | hir::ExprKind::Binary(..) + | hir::ExprKind::AddrOf(..) + | hir::ExprKind::Cast(..) + | hir::ExprKind::DropTemps(..) + | hir::ExprKind::Unary(..) + | hir::ExprKind::Break(..) + | hir::ExprKind::Continue(_) + | hir::ExprKind::Lit(_) + | hir::ExprKind::Ret(..) + | hir::ExprKind::Block(..) + | hir::ExprKind::Assign(..) + | hir::ExprKind::AssignOp(..) + | hir::ExprKind::Struct(..) + | hir::ExprKind::Repeat(..) + | hir::ExprKind::InlineAsm(..) + | hir::ExprKind::LlvmInlineAsm(..) + | hir::ExprKind::Box(..) + | hir::ExprKind::Yield(..) + | hir::ExprKind::Type(..) + | hir::ExprKind::Err + | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) + | hir::ExprKind::Path(hir::QPath::LangItem(..)) => { + intravisit::walk_expr(self, expr); + } } } } From 063d5e9d8bdf9f0b26817265ca90e246bb20e379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sun, 27 Sep 2020 00:00:00 +0000 Subject: [PATCH 6/6] liveness: Test interaction with automatically_derived attribute --- src/test/ui/liveness/liveness-derive.rs | 38 +++++++++++++++++++++ src/test/ui/liveness/liveness-derive.stderr | 15 ++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/test/ui/liveness/liveness-derive.rs create mode 100644 src/test/ui/liveness/liveness-derive.stderr diff --git a/src/test/ui/liveness/liveness-derive.rs b/src/test/ui/liveness/liveness-derive.rs new file mode 100644 index 0000000000000..66d0b7090ffb5 --- /dev/null +++ b/src/test/ui/liveness/liveness-derive.rs @@ -0,0 +1,38 @@ +// Test for interaction between #[automatically_derived] attribute used by +// built-in derives and lints generated by liveness pass. +// +// edition:2018 +// check-pass +#![warn(unused)] + +pub trait T: Sized { + const N: usize; + fn t(&self) -> Self; +} + +impl T for u32 { + const N: usize = { + let a = 0; // FIXME should warn about unused variable + 4 + }; + + fn t(&self) -> Self { + let b = 16; //~ WARN unused variable: `b` + 0 + } +} + +#[automatically_derived] +impl T for i32 { + const N: usize = { + let c = 0; + 4 + }; + + fn t(&self) -> Self { + let d = 17; + 0 + } +} + +fn main() {} diff --git a/src/test/ui/liveness/liveness-derive.stderr b/src/test/ui/liveness/liveness-derive.stderr new file mode 100644 index 0000000000000..d4f45a0a31341 --- /dev/null +++ b/src/test/ui/liveness/liveness-derive.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `b` + --> $DIR/liveness-derive.rs:20:13 + | +LL | let b = 16; + | ^ help: if this is intentional, prefix it with an underscore: `_b` + | +note: the lint level is defined here + --> $DIR/liveness-derive.rs:6:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted +