@@ -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) {
@@ -561,6 +563,11 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
561
563
}
562
564
563
565
/// Checks if the top level expression can be moved into a closure as is.
566
+ /// Currently checks for:
567
+ /// * Break/Continue outside the given jump targets
568
+ /// * Yield/Return statments.
569
+ /// * Inline assembly
570
+ /// * Usages of a field of a local where the type of the local can be partially moved.
564
571
pub fn can_move_expr_to_closure_no_visit (
565
572
cx : & LateContext < ' tcx > ,
566
573
expr : & ' tcx Expr < ' _ > ,
@@ -632,6 +639,22 @@ impl std::ops::BitOrAssign for CaptureKind {
632
639
/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
633
640
/// function argument (other than a receiver).
634
641
pub fn capture_local_usage ( cx : & LateContext < ' tcx > , e : & Expr < ' _ > ) -> CaptureKind {
642
+ fn pat_capture_kind ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) -> CaptureKind {
643
+ let mut capture = CaptureKind :: Ref ( Mutability :: Not ) ;
644
+ pat. each_binding ( |_, id, span, _| {
645
+ match cx. typeck_results ( ) . extract_binding_mode ( cx. sess ( ) , id, span) . unwrap ( ) {
646
+ BindingMode :: BindByValue ( _) if !is_copy ( cx, cx. typeck_results ( ) . node_type ( id) ) => {
647
+ capture = CaptureKind :: Value ;
648
+ } ,
649
+ BindingMode :: BindByReference ( Mutability :: Mut ) if capture != CaptureKind :: Value => {
650
+ capture = CaptureKind :: Ref ( Mutability :: Mut ) ;
651
+ } ,
652
+ _ => ( ) ,
653
+ }
654
+ } ) ;
655
+ capture
656
+ }
657
+
635
658
debug_assert ! ( matches!(
636
659
e. kind,
637
660
ExprKind :: Path ( QPath :: Resolved ( None , Path { res: Res :: Local ( _) , .. } ) )
@@ -640,6 +663,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
640
663
let map = cx. tcx . hir ( ) ;
641
664
let mut child_id = e. hir_id ;
642
665
let mut capture = CaptureKind :: Value ;
666
+ let mut capture_expr_ty = e;
643
667
644
668
for ( parent_id, parent) in map. parent_iter ( e. hir_id ) {
645
669
if let [ Adjustment {
@@ -658,23 +682,47 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
658
682
}
659
683
}
660
684
661
- if let Node :: Expr ( e ) = parent {
662
- match e. kind {
685
+ match parent {
686
+ Node :: Expr ( e ) => match e. kind {
663
687
ExprKind :: AddrOf ( _, mutability, _) => return CaptureKind :: Ref ( mutability) ,
664
688
ExprKind :: Index ( ..) | ExprKind :: Unary ( UnOp :: Deref , _) => capture = CaptureKind :: Ref ( Mutability :: Not ) ,
665
689
ExprKind :: Assign ( lhs, ..) | ExprKind :: Assign ( _, lhs, _) if lhs. hir_id == child_id => {
666
690
return CaptureKind :: Ref ( Mutability :: Mut ) ;
667
691
} ,
692
+ ExprKind :: Field ( ..) => {
693
+ if capture == CaptureKind :: Value {
694
+ capture_expr_ty = e;
695
+ }
696
+ } ,
697
+ ExprKind :: Match ( _, arms, _) => {
698
+ let mut mutability = Mutability :: Not ;
699
+ for capture in arms. iter ( ) . map ( |arm| pat_capture_kind ( cx, arm. pat ) ) {
700
+ match capture {
701
+ CaptureKind :: Value => break ,
702
+ CaptureKind :: Ref ( Mutability :: Mut ) => mutability = Mutability :: Mut ,
703
+ CaptureKind :: Ref ( Mutability :: Not ) => ( ) ,
704
+ }
705
+ }
706
+ return CaptureKind :: Ref ( mutability) ;
707
+ } ,
668
708
_ => break ,
669
- }
670
- } else {
671
- break ;
709
+ } ,
710
+ Node :: Local ( l) => match pat_capture_kind ( cx, l. pat ) {
711
+ CaptureKind :: Value => break ,
712
+ capture @ CaptureKind :: Ref ( _) => return capture,
713
+ } ,
714
+ _ => break ,
672
715
}
673
716
674
717
child_id = parent_id;
675
718
}
676
719
677
- capture
720
+ if capture == CaptureKind :: Value && is_copy ( cx, cx. typeck_results ( ) . expr_ty ( capture_expr_ty) ) {
721
+ // Copy types are never automatically captured by value.
722
+ CaptureKind :: Ref ( Mutability :: Not )
723
+ } else {
724
+ capture
725
+ }
678
726
}
679
727
680
728
/// Checks if the expression can be moved into a closure as is. This will return a list of captures
@@ -710,6 +758,31 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) ->
710
758
self . captures . entry ( l) . and_modify ( |e| * e |= cap) . or_insert ( cap) ;
711
759
}
712
760
} ,
761
+ ExprKind :: Closure ( ..) => {
762
+ let closure_id = self . cx . tcx . hir ( ) . local_def_id ( e. hir_id ) . to_def_id ( ) ;
763
+ for capture in self . cx . typeck_results ( ) . closure_min_captures_flattened ( closure_id) {
764
+ let local_id = match capture. place . base {
765
+ PlaceBase :: Local ( id) => id,
766
+ PlaceBase :: Upvar ( var) => var. var_path . hir_id ,
767
+ _ => continue ,
768
+ } ;
769
+ if !self . locals . contains ( & local_id) {
770
+ let capture = match capture. info . capture_kind {
771
+ UpvarCapture :: ByValue ( _) => CaptureKind :: Value ,
772
+ UpvarCapture :: ByRef ( borrow) => match borrow. kind {
773
+ BorrowKind :: ImmBorrow => CaptureKind :: Ref ( Mutability :: Not ) ,
774
+ BorrowKind :: UniqueImmBorrow | BorrowKind :: MutBorrow => {
775
+ CaptureKind :: Ref ( Mutability :: Mut )
776
+ } ,
777
+ } ,
778
+ } ;
779
+ self . captures
780
+ . entry ( local_id)
781
+ . and_modify ( |e| * e |= capture)
782
+ . or_insert ( capture) ;
783
+ }
784
+ }
785
+ } ,
713
786
ExprKind :: Loop ( b, ..) => {
714
787
self . loops . push ( e. hir_id ) ;
715
788
self . visit_block ( b) ;
@@ -1763,7 +1836,7 @@ pub fn peel_hir_expr_while<'tcx>(
1763
1836
pub fn peel_n_hir_expr_refs ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
1764
1837
let mut remaining = count;
1765
1838
let e = peel_hir_expr_while ( expr, |e| match e. kind {
1766
- ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) if remaining != 0 => {
1839
+ ExprKind :: AddrOf ( ast :: BorrowKind :: Ref , _, e) if remaining != 0 => {
1767
1840
remaining -= 1 ;
1768
1841
Some ( e)
1769
1842
} ,
@@ -1777,7 +1850,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
1777
1850
pub fn peel_hir_expr_refs ( expr : & ' a Expr < ' a > ) -> ( & ' a Expr < ' a > , usize ) {
1778
1851
let mut count = 0 ;
1779
1852
let e = peel_hir_expr_while ( expr, |e| match e. kind {
1780
- ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) => {
1853
+ ExprKind :: AddrOf ( ast :: BorrowKind :: Ref , _, e) => {
1781
1854
count += 1 ;
1782
1855
Some ( e)
1783
1856
} ,
0 commit comments