@@ -62,7 +62,7 @@ use std::collections::hash_map::Entry;
62
62
use std:: hash:: BuildHasherDefault ;
63
63
64
64
use if_chain:: if_chain;
65
- use rustc_ast:: ast:: { self , Attribute , BorrowKind , LitKind } ;
65
+ use rustc_ast:: ast:: { self , Attribute , LitKind } ;
66
66
use rustc_data_structures:: unhash:: UnhashMap ;
67
67
use rustc_hir as hir;
68
68
use rustc_hir:: def:: { DefKind , Res } ;
@@ -78,9 +78,11 @@ use rustc_hir::{
78
78
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
79
79
use rustc_middle:: hir:: exports:: Export ;
80
80
use rustc_middle:: hir:: map:: Map ;
81
+ use rustc_middle:: hir:: place:: PlaceBase ;
81
82
use rustc_middle:: ty as rustc_ty;
82
83
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow } ;
83
- use rustc_middle:: ty:: { layout:: IntegerExt , DefIdTree , Ty , TyCtxt , TypeAndMut , TypeFoldable } ;
84
+ use rustc_middle:: ty:: binding:: BindingMode ;
85
+ use rustc_middle:: ty:: { layout:: IntegerExt , BorrowKind , DefIdTree , Ty , TyCtxt , TypeAndMut , TypeFoldable , UpvarCapture } ;
84
86
use rustc_semver:: RustcVersion ;
85
87
use rustc_session:: Session ;
86
88
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
@@ -91,7 +93,7 @@ use rustc_span::{Span, DUMMY_SP};
91
93
use rustc_target:: abi:: Integer ;
92
94
93
95
use crate :: consts:: { constant, Constant } ;
94
- use crate :: ty:: { can_partially_move_ty, is_recursively_primitive_type} ;
96
+ use crate :: ty:: { can_partially_move_ty, is_copy , is_recursively_primitive_type} ;
95
97
96
98
pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
97
99
if let Ok ( version) = RustcVersion :: parse ( msrv) {
@@ -628,6 +630,11 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
628
630
}
629
631
630
632
/// Checks if the top level expression can be moved into a closure as is.
633
+ /// Currently checks for:
634
+ /// * Break/Continue outside the given jump targets
635
+ /// * Yield/Return statments.
636
+ /// * Inline assembly
637
+ /// * Usages of a field of a local where the type of the local can be partially moved.
631
638
pub fn can_move_expr_to_closure_no_visit (
632
639
cx : & LateContext < ' tcx > ,
633
640
expr : & ' tcx Expr < ' _ > ,
@@ -699,6 +706,22 @@ impl std::ops::BitOrAssign for CaptureKind {
699
706
/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
700
707
/// function argument (other than a receiver).
701
708
pub fn capture_local_usage ( cx : & LateContext < ' tcx > , e : & Expr < ' _ > ) -> CaptureKind {
709
+ fn pat_capture_kind ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) -> CaptureKind {
710
+ let mut capture = CaptureKind :: Ref ( Mutability :: Not ) ;
711
+ pat. each_binding ( |_, id, span, _| {
712
+ match cx. typeck_results ( ) . extract_binding_mode ( cx. sess ( ) , id, span) . unwrap ( ) {
713
+ BindingMode :: BindByValue ( _) if !is_copy ( cx, cx. typeck_results ( ) . node_type ( id) ) => {
714
+ capture = CaptureKind :: Value ;
715
+ } ,
716
+ BindingMode :: BindByReference ( Mutability :: Mut ) if capture != CaptureKind :: Value => {
717
+ capture = CaptureKind :: Ref ( Mutability :: Mut ) ;
718
+ } ,
719
+ _ => ( ) ,
720
+ }
721
+ } ) ;
722
+ capture
723
+ }
724
+
702
725
debug_assert ! ( matches!(
703
726
e. kind,
704
727
ExprKind :: Path ( QPath :: Resolved ( None , Path { res: Res :: Local ( _) , .. } ) )
@@ -707,6 +730,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
707
730
let map = cx. tcx . hir ( ) ;
708
731
let mut child_id = e. hir_id ;
709
732
let mut capture = CaptureKind :: Value ;
733
+ let mut capture_expr_ty = e;
710
734
711
735
for ( parent_id, parent) in map. parent_iter ( e. hir_id ) {
712
736
if let [ Adjustment {
@@ -725,23 +749,47 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
725
749
}
726
750
}
727
751
728
- if let Node :: Expr ( e ) = parent {
729
- match e. kind {
752
+ match parent {
753
+ Node :: Expr ( e ) => match e. kind {
730
754
ExprKind :: AddrOf ( _, mutability, _) => return CaptureKind :: Ref ( mutability) ,
731
755
ExprKind :: Index ( ..) | ExprKind :: Unary ( UnOp :: Deref , _) => capture = CaptureKind :: Ref ( Mutability :: Not ) ,
732
756
ExprKind :: Assign ( lhs, ..) | ExprKind :: Assign ( _, lhs, _) if lhs. hir_id == child_id => {
733
757
return CaptureKind :: Ref ( Mutability :: Mut ) ;
734
758
} ,
759
+ ExprKind :: Field ( ..) => {
760
+ if capture == CaptureKind :: Value {
761
+ capture_expr_ty = e;
762
+ }
763
+ } ,
764
+ ExprKind :: Match ( _, arms, _) => {
765
+ let mut mutability = Mutability :: Not ;
766
+ for capture in arms. iter ( ) . map ( |arm| pat_capture_kind ( cx, arm. pat ) ) {
767
+ match capture {
768
+ CaptureKind :: Value => break ,
769
+ CaptureKind :: Ref ( Mutability :: Mut ) => mutability = Mutability :: Mut ,
770
+ CaptureKind :: Ref ( Mutability :: Not ) => ( ) ,
771
+ }
772
+ }
773
+ return CaptureKind :: Ref ( mutability) ;
774
+ } ,
735
775
_ => break ,
736
- }
737
- } else {
738
- break ;
776
+ } ,
777
+ Node :: Local ( l) => match pat_capture_kind ( cx, l. pat ) {
778
+ CaptureKind :: Value => break ,
779
+ capture @ CaptureKind :: Ref ( _) => return capture,
780
+ } ,
781
+ _ => break ,
739
782
}
740
783
741
784
child_id = parent_id;
742
785
}
743
786
744
- capture
787
+ if capture == CaptureKind :: Value && is_copy ( cx, cx. typeck_results ( ) . expr_ty ( capture_expr_ty) ) {
788
+ // Copy types are never automatically captured by value.
789
+ CaptureKind :: Ref ( Mutability :: Not )
790
+ } else {
791
+ capture
792
+ }
745
793
}
746
794
747
795
/// Checks if the expression can be moved into a closure as is. This will return a list of captures
@@ -777,6 +825,31 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) ->
777
825
self . captures . entry ( l) . and_modify ( |e| * e |= cap) . or_insert ( cap) ;
778
826
}
779
827
} ,
828
+ ExprKind :: Closure ( ..) => {
829
+ let closure_id = self . cx . tcx . hir ( ) . local_def_id ( e. hir_id ) . to_def_id ( ) ;
830
+ for capture in self . cx . typeck_results ( ) . closure_min_captures_flattened ( closure_id) {
831
+ let local_id = match capture. place . base {
832
+ PlaceBase :: Local ( id) => id,
833
+ PlaceBase :: Upvar ( var) => var. var_path . hir_id ,
834
+ _ => continue ,
835
+ } ;
836
+ if !self . locals . contains ( & local_id) {
837
+ let capture = match capture. info . capture_kind {
838
+ UpvarCapture :: ByValue ( _) => CaptureKind :: Value ,
839
+ UpvarCapture :: ByRef ( borrow) => match borrow. kind {
840
+ BorrowKind :: ImmBorrow => CaptureKind :: Ref ( Mutability :: Not ) ,
841
+ BorrowKind :: UniqueImmBorrow | BorrowKind :: MutBorrow => {
842
+ CaptureKind :: Ref ( Mutability :: Mut )
843
+ } ,
844
+ } ,
845
+ } ;
846
+ self . captures
847
+ . entry ( local_id)
848
+ . and_modify ( |e| * e |= capture)
849
+ . or_insert ( capture) ;
850
+ }
851
+ }
852
+ } ,
780
853
ExprKind :: Loop ( b, ..) => {
781
854
self . loops . push ( e. hir_id ) ;
782
855
self . visit_block ( b) ;
@@ -1830,7 +1903,7 @@ pub fn peel_hir_expr_while<'tcx>(
1830
1903
pub fn peel_n_hir_expr_refs ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
1831
1904
let mut remaining = count;
1832
1905
let e = peel_hir_expr_while ( expr, |e| match e. kind {
1833
- ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) if remaining != 0 => {
1906
+ ExprKind :: AddrOf ( ast :: BorrowKind :: Ref , _, e) if remaining != 0 => {
1834
1907
remaining -= 1 ;
1835
1908
Some ( e)
1836
1909
} ,
@@ -1844,7 +1917,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
1844
1917
pub fn peel_hir_expr_refs ( expr : & ' a Expr < ' a > ) -> ( & ' a Expr < ' a > , usize ) {
1845
1918
let mut count = 0 ;
1846
1919
let e = peel_hir_expr_while ( expr, |e| match e. kind {
1847
- ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) => {
1920
+ ExprKind :: AddrOf ( ast :: BorrowKind :: Ref , _, e) => {
1848
1921
count += 1 ;
1849
1922
Some ( e)
1850
1923
} ,
0 commit comments