7676//! is not just used to generate a new value. For example, `x += 1` is
7777//! a read but not a use. This is used to generate better warnings.
7878//!
79- //! ## Special Variables
79+ //! ## Special nodes and variables
8080//!
81- //! We generate various special variables for various, well, special purposes.
82- //! These are described in the `specials` struct:
83- //!
84- //! - `exit_ln`: a live node that is generated to represent every 'exit' from
85- //! the function, whether it be by explicit return, panic, or other means.
81+ //! We generate various special nodes for various, well, special purposes.
82+ //! These are described in the `Specials` struct.
8683
8784use self :: LiveNodeKind :: * ;
8885use self :: VarKind :: * ;
@@ -131,6 +128,7 @@ enum LiveNodeKind {
131128 UpvarNode ( Span ) ,
132129 ExprNode ( Span ) ,
133130 VarDefNode ( Span ) ,
131+ ClosureNode ,
134132 ExitNode ,
135133}
136134
@@ -140,6 +138,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
140138 UpvarNode ( s) => format ! ( "Upvar node [{}]" , sm. span_to_string( s) ) ,
141139 ExprNode ( s) => format ! ( "Expr node [{}]" , sm. span_to_string( s) ) ,
142140 VarDefNode ( s) => format ! ( "Var def node [{}]" , sm. span_to_string( s) ) ,
141+ ClosureNode => "Closure node" . to_owned ( ) ,
143142 ExitNode => "Exit node" . to_owned ( ) ,
144143 }
145144}
@@ -396,10 +395,12 @@ fn visit_fn<'tcx>(
396395
397396 // compute liveness
398397 let mut lsets = Liveness :: new ( & mut fn_maps, def_id) ;
399- let entry_ln = lsets. compute ( & body. value ) ;
398+ let entry_ln = lsets. compute ( fk, & body, sp, id) ;
399+ lsets. log_liveness ( entry_ln, id) ;
400400
401401 // check for various error conditions
402402 lsets. visit_body ( body) ;
403+ lsets. warn_about_unused_upvars ( entry_ln) ;
403404 lsets. warn_about_unused_args ( body, entry_ln) ;
404405}
405406
@@ -634,6 +635,12 @@ impl RWUTable {
634635
635636#[ derive( Copy , Clone ) ]
636637struct Specials {
638+ /// A live node representing a point of execution before closure entry &
639+ /// after closure exit. Used to calculate liveness of captured variables
640+ /// through calls to the same closure. Used for Fn & FnMut closures only.
641+ closure_ln : LiveNode ,
642+ /// A live node representing every 'exit' from the function, whether it be
643+ /// by explicit return, panic, or other means.
637644 exit_ln : LiveNode ,
638645}
639646
@@ -658,11 +665,8 @@ struct Liveness<'a, 'tcx> {
658665
659666impl < ' a , ' tcx > Liveness < ' a , ' tcx > {
660667 fn new ( ir : & ' a mut IrMaps < ' tcx > , def_id : LocalDefId ) -> Liveness < ' a , ' tcx > {
661- // Special nodes and variables:
662- // - exit_ln represents the end of the fn, either by return or panic
663- // - implicit_ret_var is a pseudo-variable that represents
664- // an implicit return
665668 let specials = Specials {
669+ closure_ln : ir. add_live_node ( ClosureNode ) ,
666670 exit_ln : ir. add_live_node ( ExitNode ) ,
667671 } ;
668672
@@ -789,6 +793,20 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
789793 String :: from_utf8 ( wr) . unwrap ( )
790794 }
791795
796+ fn log_liveness ( & self , entry_ln : LiveNode , hir_id : hir:: HirId ) {
797+ // hack to skip the loop unless debug! is enabled:
798+ debug ! (
799+ "^^ liveness computation results for body {} (entry={:?})" ,
800+ {
801+ for ln_idx in 0 ..self . ir. num_live_nodes {
802+ debug!( "{:?}" , self . ln_str( LiveNode ( ln_idx as u32 ) ) ) ;
803+ }
804+ hir_id
805+ } ,
806+ entry_ln
807+ ) ;
808+ }
809+
792810 fn init_empty ( & mut self , ln : LiveNode , succ_ln : LiveNode ) {
793811 self . successors [ ln. get ( ) ] = succ_ln;
794812
@@ -889,33 +907,87 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
889907 self . rwu_table . assign_unpacked ( idx, rwu) ;
890908 }
891909
892- fn compute ( & mut self , body : & hir:: Expr < ' _ > ) -> LiveNode {
893- debug ! ( "compute: using id for body, {:?}" , body) ;
910+ fn compute (
911+ & mut self ,
912+ fk : FnKind < ' _ > ,
913+ body : & hir:: Body < ' _ > ,
914+ span : Span ,
915+ id : hir:: HirId ,
916+ ) -> LiveNode {
917+ debug ! ( "compute: using id for body, {:?}" , body. value) ;
894918
895- let s = self . s ;
919+ // # Liveness of captured variables
920+ //
921+ // When computing the liveness for captured variables we take into
922+ // account how variable is captured (ByRef vs ByValue) and what is the
923+ // closure kind (Generator / FnOnce vs Fn / FnMut).
924+ //
925+ // Variables captured by reference are assumed to be used on the exit
926+ // from the closure.
927+ //
928+ // In FnOnce closures, variables captured by value are known to be dead
929+ // on exit since it is impossible to call the closure again.
930+ //
931+ // In Fn / FnMut closures, variables captured by value are live on exit
932+ // if they are live on the entry to the closure, since only the closure
933+ // itself can access them on subsequent calls.
896934
897935 if let Some ( upvars) = self . ir . tcx . upvars_mentioned ( self . ir . body_owner ) {
936+ // Mark upvars captured by reference as used after closure exits.
898937 for ( & var_hir_id, upvar) in upvars. iter ( ) . rev ( ) {
899- let var = self . variable ( var_hir_id, upvar. span ) ;
900- self . acc ( s. exit_ln , var, ACC_READ | ACC_USE ) ;
938+ let upvar_id = ty:: UpvarId {
939+ var_path : ty:: UpvarPath { hir_id : var_hir_id } ,
940+ closure_expr_id : self . ir . body_owner . expect_local ( ) ,
941+ } ;
942+ match self . tables . upvar_capture ( upvar_id) {
943+ ty:: UpvarCapture :: ByRef ( _) => {
944+ let var = self . variable ( var_hir_id, upvar. span ) ;
945+ self . acc ( self . s . exit_ln , var, ACC_READ | ACC_USE ) ;
946+ }
947+ ty:: UpvarCapture :: ByValue => { }
948+ }
901949 }
902950 }
903951
904- let entry_ln = self . propagate_through_expr ( body, s. exit_ln ) ;
952+ let succ = self . propagate_through_expr ( & body. value , self . s . exit_ln ) ;
905953
906- // hack to skip the loop unless debug! is enabled:
907- debug ! (
908- "^^ liveness computation results for body {} (entry={:?})" ,
909- {
910- for ln_idx in 0 ..self . ir. num_live_nodes {
911- debug!( "{:?}" , self . ln_str( LiveNode ( ln_idx as u32 ) ) ) ;
912- }
913- body. hir_id
954+ match fk {
955+ FnKind :: Method ( ..) | FnKind :: ItemFn ( ..) => return succ,
956+ FnKind :: Closure ( ..) => { }
957+ }
958+
959+ let ty = self . tables . node_type ( id) ;
960+ match ty. kind {
961+ ty:: Closure ( _def_id, substs) => match substs. as_closure ( ) . kind ( ) {
962+ ty:: ClosureKind :: Fn => { }
963+ ty:: ClosureKind :: FnMut => { }
964+ ty:: ClosureKind :: FnOnce => return succ,
914965 } ,
915- entry_ln
916- ) ;
966+ ty:: Generator ( ..) => return succ,
967+ _ => {
968+ span_bug ! ( span, "type of closure expr {:?} is not a closure {:?}" , id, ty, ) ;
969+ }
970+ } ;
917971
918- entry_ln
972+ // Propagate through calls to the closure.
973+ let mut first_merge = true ;
974+ loop {
975+ self . init_from_succ ( self . s . closure_ln , succ) ;
976+ for param in body. params {
977+ param. pat . each_binding ( |_bm, hir_id, _x, ident| {
978+ let var = self . variable ( hir_id, ident. span ) ;
979+ self . define ( self . s . closure_ln , var) ;
980+ } )
981+ }
982+
983+ if !self . merge_from_succ ( self . s . exit_ln , self . s . closure_ln , first_merge) {
984+ break ;
985+ }
986+ first_merge = false ;
987+ assert_eq ! ( succ, self . propagate_through_expr( & body. value, self . s. exit_ln) ) ;
988+ }
989+
990+ succ
919991 }
920992
921993 fn propagate_through_block ( & mut self , blk : & hir:: Block < ' _ > , succ : LiveNode ) -> LiveNode {
@@ -1533,11 +1605,60 @@ impl<'tcx> Liveness<'_, 'tcx> {
15331605 if name. is_empty ( ) || name. as_bytes ( ) [ 0 ] == b'_' { None } else { Some ( name) }
15341606 }
15351607
1608+ fn warn_about_unused_upvars ( & self , entry_ln : LiveNode ) {
1609+ let upvars = match self . ir . tcx . upvars_mentioned ( self . ir . body_owner ) {
1610+ None => return ,
1611+ Some ( upvars) => upvars,
1612+ } ;
1613+ for ( & var_hir_id, upvar) in upvars. iter ( ) {
1614+ let var = self . variable ( var_hir_id, upvar. span ) ;
1615+ let upvar_id = ty:: UpvarId {
1616+ var_path : ty:: UpvarPath { hir_id : var_hir_id } ,
1617+ closure_expr_id : self . ir . body_owner . expect_local ( ) ,
1618+ } ;
1619+ match self . tables . upvar_capture ( upvar_id) {
1620+ ty:: UpvarCapture :: ByValue => { }
1621+ ty:: UpvarCapture :: ByRef ( ..) => continue ,
1622+ } ;
1623+ if self . used_on_entry ( entry_ln, var) {
1624+ if self . live_on_entry ( entry_ln, var) . is_none ( ) {
1625+ if let Some ( name) = self . should_warn ( var) {
1626+ self . ir . tcx . struct_span_lint_hir (
1627+ lint:: builtin:: UNUSED_ASSIGNMENTS ,
1628+ var_hir_id,
1629+ vec ! [ upvar. span] ,
1630+ |lint| {
1631+ lint. build ( & format ! ( "value captured by `{}` is never read" , name) )
1632+ . help ( "did you mean to capture by reference instead?" )
1633+ . emit ( ) ;
1634+ } ,
1635+ ) ;
1636+ }
1637+ }
1638+ } else {
1639+ if let Some ( name) = self . should_warn ( var) {
1640+ self . ir . tcx . struct_span_lint_hir (
1641+ lint:: builtin:: UNUSED_VARIABLES ,
1642+ var_hir_id,
1643+ vec ! [ upvar. span] ,
1644+ |lint| {
1645+ lint. build ( & format ! ( "unused variable: `{}`" , name) )
1646+ . help ( "did you mean to capture by reference instead?" )
1647+ . emit ( ) ;
1648+ } ,
1649+ ) ;
1650+ }
1651+ }
1652+ }
1653+ }
1654+
15361655 fn warn_about_unused_args ( & self , body : & hir:: Body < ' _ > , entry_ln : LiveNode ) {
15371656 for p in body. params {
15381657 self . check_unused_vars_in_pat ( & p. pat , Some ( entry_ln) , |spans, hir_id, ln, var| {
15391658 if self . live_on_entry ( ln, var) . is_none ( ) {
1540- self . report_dead_assign ( hir_id, spans, var, true ) ;
1659+ self . report_unsed_assign ( hir_id, spans, var, |name| {
1660+ format ! ( "value passed to `{}` is never read" , name)
1661+ } ) ;
15411662 }
15421663 } ) ;
15431664 }
@@ -1651,35 +1772,30 @@ impl<'tcx> Liveness<'_, 'tcx> {
16511772
16521773 fn warn_about_dead_assign ( & self , spans : Vec < Span > , hir_id : HirId , ln : LiveNode , var : Variable ) {
16531774 if self . live_on_exit ( ln, var) . is_none ( ) {
1654- self . report_dead_assign ( hir_id, spans, var, false ) ;
1775+ self . report_unsed_assign ( hir_id, spans, var, |name| {
1776+ format ! ( "value assigned to `{}` is never read" , name)
1777+ } ) ;
16551778 }
16561779 }
16571780
1658- fn report_dead_assign ( & self , hir_id : HirId , spans : Vec < Span > , var : Variable , is_param : bool ) {
1781+ fn report_unsed_assign (
1782+ & self ,
1783+ hir_id : HirId ,
1784+ spans : Vec < Span > ,
1785+ var : Variable ,
1786+ message : impl Fn ( & str ) -> String ,
1787+ ) {
16591788 if let Some ( name) = self . should_warn ( var) {
1660- if is_param {
1661- self . ir . tcx . struct_span_lint_hir (
1662- lint:: builtin:: UNUSED_ASSIGNMENTS ,
1663- hir_id,
1664- spans,
1665- |lint| {
1666- lint. build ( & format ! ( "value passed to `{}` is never read" , name) )
1667- . help ( "maybe it is overwritten before being read?" )
1668- . emit ( ) ;
1669- } ,
1670- )
1671- } else {
1672- self . ir . tcx . struct_span_lint_hir (
1673- lint:: builtin:: UNUSED_ASSIGNMENTS ,
1674- hir_id,
1675- spans,
1676- |lint| {
1677- lint. build ( & format ! ( "value assigned to `{}` is never read" , name) )
1678- . help ( "maybe it is overwritten before being read?" )
1679- . emit ( ) ;
1680- } ,
1681- )
1682- }
1789+ self . ir . tcx . struct_span_lint_hir (
1790+ lint:: builtin:: UNUSED_ASSIGNMENTS ,
1791+ hir_id,
1792+ spans,
1793+ |lint| {
1794+ lint. build ( & message ( & name) )
1795+ . help ( "maybe it is overwritten before being read?" )
1796+ . emit ( ) ;
1797+ } ,
1798+ )
16831799 }
16841800 }
16851801}
0 commit comments