@@ -62,7 +62,7 @@ use std::collections::hash_map::Entry;
6262use std:: hash:: BuildHasherDefault ;
6363
6464use if_chain:: if_chain;
65- use rustc_ast:: ast:: { self , Attribute , BorrowKind , LitKind } ;
65+ use rustc_ast:: ast:: { self , Attribute , LitKind } ;
6666use rustc_data_structures:: unhash:: UnhashMap ;
6767use rustc_hir as hir;
6868use rustc_hir:: def:: { DefKind , Res } ;
@@ -78,9 +78,11 @@ use rustc_hir::{
7878use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
7979use rustc_middle:: hir:: exports:: Export ;
8080use rustc_middle:: hir:: map:: Map ;
81+ use rustc_middle:: hir:: place:: PlaceBase ;
8182use rustc_middle:: ty as rustc_ty;
8283use 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 } ;
8486use rustc_semver:: RustcVersion ;
8587use rustc_session:: Session ;
8688use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
@@ -91,7 +93,7 @@ use rustc_span::{Span, DUMMY_SP};
9193use rustc_target:: abi:: Integer ;
9294
9395use 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} ;
9597
9698pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
9799 if let Ok ( version) = RustcVersion :: parse ( msrv) {
@@ -628,6 +630,11 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
628630}
629631
630632/// 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.
631638pub fn can_move_expr_to_closure_no_visit (
632639 cx : & LateContext < ' tcx > ,
633640 expr : & ' tcx Expr < ' _ > ,
@@ -699,6 +706,22 @@ impl std::ops::BitOrAssign for CaptureKind {
699706/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
700707/// function argument (other than a receiver).
701708pub 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+
702725 debug_assert ! ( matches!(
703726 e. kind,
704727 ExprKind :: Path ( QPath :: Resolved ( None , Path { res: Res :: Local ( _) , .. } ) )
@@ -707,6 +730,7 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
707730 let map = cx. tcx . hir ( ) ;
708731 let mut child_id = e. hir_id ;
709732 let mut capture = CaptureKind :: Value ;
733+ let mut capture_expr_ty = e;
710734
711735 for ( parent_id, parent) in map. parent_iter ( e. hir_id ) {
712736 if let [ Adjustment {
@@ -725,23 +749,47 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind
725749 }
726750 }
727751
728- if let Node :: Expr ( e ) = parent {
729- match e. kind {
752+ match parent {
753+ Node :: Expr ( e ) => match e. kind {
730754 ExprKind :: AddrOf ( _, mutability, _) => return CaptureKind :: Ref ( mutability) ,
731755 ExprKind :: Index ( ..) | ExprKind :: Unary ( UnOp :: Deref , _) => capture = CaptureKind :: Ref ( Mutability :: Not ) ,
732756 ExprKind :: Assign ( lhs, ..) | ExprKind :: Assign ( _, lhs, _) if lhs. hir_id == child_id => {
733757 return CaptureKind :: Ref ( Mutability :: Mut ) ;
734758 } ,
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+ } ,
735775 _ => 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 ,
739782 }
740783
741784 child_id = parent_id;
742785 }
743786
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+ }
745793}
746794
747795/// 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<'_>) ->
777825 self . captures . entry ( l) . and_modify ( |e| * e |= cap) . or_insert ( cap) ;
778826 }
779827 } ,
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+ } ,
780853 ExprKind :: Loop ( b, ..) => {
781854 self . loops . push ( e. hir_id ) ;
782855 self . visit_block ( b) ;
@@ -1830,7 +1903,7 @@ pub fn peel_hir_expr_while<'tcx>(
18301903pub fn peel_n_hir_expr_refs ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
18311904 let mut remaining = count;
18321905 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 => {
18341907 remaining -= 1 ;
18351908 Some ( e)
18361909 } ,
@@ -1844,7 +1917,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
18441917pub fn peel_hir_expr_refs ( expr : & ' a Expr < ' a > ) -> ( & ' a Expr < ' a > , usize ) {
18451918 let mut count = 0 ;
18461919 let e = peel_hir_expr_while ( expr, |e| match e. kind {
1847- ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) => {
1920+ ExprKind :: AddrOf ( ast :: BorrowKind :: Ref , _, e) => {
18481921 count += 1 ;
18491922 Some ( e)
18501923 } ,
0 commit comments