@@ -350,9 +350,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
350350
351351 for ( place, mut capture_info) in capture_information {
352352 // Apply rules for safety before inferring closure kind
353- let place = restrict_capture_precision ( place) ;
353+ let ( place, capture_kind) =
354+ restrict_capture_precision ( place, capture_info. capture_kind ) ;
355+ capture_info. capture_kind = capture_kind;
354356
355- let place = truncate_capture_for_optimization ( & place) ;
357+ let ( place, capture_kind) =
358+ truncate_capture_for_optimization ( place, capture_info. capture_kind ) ;
359+ capture_info. capture_kind = capture_kind;
356360
357361 let usage_span = if let Some ( usage_expr) = capture_info. path_expr_id {
358362 self . tcx . hir ( ) . span ( usage_expr)
@@ -520,8 +524,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
520524 // current place is ancestor of possible_descendant
521525 PlaceAncestryRelation :: Ancestor => {
522526 descendant_found = true ;
527+
528+ let mut possible_descendant = possible_descendant. clone ( ) ;
523529 let backup_path_expr_id = updated_capture_info. path_expr_id ;
524530
531+ // Truncate the descendant (already in min_captures) to be same as the ancestor to handle any
532+ // possible change in capture mode.
533+ let ( _, descendant_capture_kind) = truncate_place_to_len (
534+ possible_descendant. place ,
535+ possible_descendant. info . capture_kind ,
536+ place. projections . len ( ) ,
537+ ) ;
538+
539+ possible_descendant. info . capture_kind = descendant_capture_kind;
540+
525541 updated_capture_info =
526542 determine_capture_info ( updated_capture_info, possible_descendant. info ) ;
527543
@@ -542,8 +558,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
542558 PlaceAncestryRelation :: Descendant => {
543559 ancestor_found = true ;
544560 let backup_path_expr_id = possible_ancestor. info . path_expr_id ;
545- possible_ancestor. info =
546- determine_capture_info ( possible_ancestor. info , capture_info) ;
561+
562+ // Truncate the descendant (current place) to be same as the ancestor to handle any
563+ // possible change in capture mode.
564+ let ( _, descendant_capture_kind) = truncate_place_to_len (
565+ place. clone ( ) ,
566+ updated_capture_info. capture_kind ,
567+ possible_ancestor. place . projections . len ( ) ,
568+ ) ;
569+
570+ updated_capture_info. capture_kind = descendant_capture_kind;
571+
572+ possible_ancestor. info = determine_capture_info (
573+ possible_ancestor. info ,
574+ updated_capture_info,
575+ ) ;
547576
548577 // we need to keep the ancestor's `path_expr_id`
549578 possible_ancestor. info . path_expr_id = backup_path_expr_id;
@@ -1412,7 +1441,8 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
14121441 tcx : TyCtxt < ' tcx > ,
14131442 param_env : ty:: ParamEnv < ' tcx > ,
14141443 place : & Place < ' tcx > ,
1415- ) -> Place < ' tcx > {
1444+ curr_borrow_kind : ty:: UpvarCapture < ' tcx > ,
1445+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
14161446 let pos = place. projections . iter ( ) . enumerate ( ) . position ( |( i, p) | {
14171447 let ty = place. ty_before_projection ( i) ;
14181448
@@ -1443,13 +1473,13 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
14431473 }
14441474 } ) ;
14451475
1446- let mut place = place. clone ( ) ;
1476+ let place = place. clone ( ) ;
14471477
14481478 if let Some ( pos) = pos {
1449- place. projections . truncate ( pos) ;
1479+ truncate_place_to_len ( place, curr_borrow_kind, pos)
1480+ } else {
1481+ ( place, curr_borrow_kind)
14501482 }
1451-
1452- place
14531483}
14541484
14551485/// Returns a Ty that applies the specified capture kind on the provided capture Ty
@@ -1570,20 +1600,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
15701600 ) ;
15711601
15721602 if let PlaceBase :: Upvar ( _) = place_with_id. place . base {
1573- let mut borrow_kind = ty:: MutBorrow ;
1574- for pointer_ty in place_with_id. place . deref_tys ( ) {
1575- match pointer_ty. kind ( ) {
1576- // Raw pointers don't inherit mutability.
1577- ty:: RawPtr ( _) => return ,
1578- // assignment to deref of an `&mut`
1579- // borrowed pointer implies that the
1580- // pointer itself must be unique, but not
1581- // necessarily *mutable*
1582- ty:: Ref ( .., hir:: Mutability :: Mut ) => borrow_kind = ty:: UniqueImmBorrow ,
1583- _ => ( ) ,
1584- }
1603+ // Raw pointers don't inherit mutability
1604+ if place_with_id. place . deref_tys ( ) . any ( ty:: TyS :: is_unsafe_ptr) {
1605+ return ;
15851606 }
1586- self . adjust_upvar_deref ( place_with_id, diag_expr_id, borrow_kind ) ;
1607+ self . adjust_upvar_deref ( place_with_id, diag_expr_id, ty :: MutBorrow ) ;
15871608 }
15881609 }
15891610
@@ -1700,9 +1721,19 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17001721 if let PlaceBase :: Upvar ( _) = place. base {
17011722 // We need to restrict Fake Read precision to avoid fake reading unsafe code,
17021723 // such as deref of a raw pointer.
1703- let place = restrict_capture_precision ( place) ;
1704- let place =
1705- restrict_repr_packed_field_ref_capture ( self . fcx . tcx , self . fcx . param_env , & place) ;
1724+ let dummy_capture_kind = ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow {
1725+ kind : ty:: BorrowKind :: ImmBorrow ,
1726+ region : & ty:: ReErased ,
1727+ } ) ;
1728+
1729+ let ( place, _) = restrict_capture_precision ( place, dummy_capture_kind) ;
1730+
1731+ let ( place, _) = restrict_repr_packed_field_ref_capture (
1732+ self . fcx . tcx ,
1733+ self . fcx . param_env ,
1734+ & place,
1735+ dummy_capture_kind,
1736+ ) ;
17061737 self . fake_reads . push ( ( place, cause, diag_expr_id) ) ;
17071738 }
17081739 }
@@ -1728,13 +1759,18 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17281759 place_with_id, diag_expr_id, bk
17291760 ) ;
17301761
1762+ // The region here will get discarded/ignored
1763+ let dummy_capture_kind =
1764+ ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow { kind : bk, region : & ty:: ReErased } ) ;
1765+
17311766 // We only want repr packed restriction to be applied to reading references into a packed
17321767 // struct, and not when the data is being moved. Therefore we call this method here instead
17331768 // of in `restrict_capture_precision`.
1734- let place = restrict_repr_packed_field_ref_capture (
1769+ let ( place, updated_kind ) = restrict_repr_packed_field_ref_capture (
17351770 self . fcx . tcx ,
17361771 self . fcx . param_env ,
17371772 & place_with_id. place ,
1773+ dummy_capture_kind,
17381774 ) ;
17391775
17401776 let place_with_id = PlaceWithHirId { place, ..* place_with_id } ;
@@ -1743,14 +1779,19 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17431779 self . init_capture_info_for_place ( & place_with_id, diag_expr_id) ;
17441780 }
17451781
1746- match bk {
1747- ty:: ImmBorrow => { }
1748- ty:: UniqueImmBorrow => {
1749- self . adjust_upvar_borrow_kind_for_unique ( & place_with_id, diag_expr_id) ;
1750- }
1751- ty:: MutBorrow => {
1752- self . adjust_upvar_borrow_kind_for_mut ( & place_with_id, diag_expr_id) ;
1753- }
1782+ match updated_kind {
1783+ ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow { kind, .. } ) => match kind {
1784+ ty:: ImmBorrow => { }
1785+ ty:: UniqueImmBorrow => {
1786+ self . adjust_upvar_borrow_kind_for_unique ( & place_with_id, diag_expr_id) ;
1787+ }
1788+ ty:: MutBorrow => {
1789+ self . adjust_upvar_borrow_kind_for_mut ( & place_with_id, diag_expr_id) ;
1790+ }
1791+ } ,
1792+
1793+ // Just truncating the place will never cause capture kind to be updated to ByValue
1794+ ty:: UpvarCapture :: ByValue ( ..) => unreachable ! ( ) ,
17541795 }
17551796 }
17561797
@@ -1764,72 +1805,73 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17641805/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
17651806/// them completely.
17661807/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
1767- fn restrict_precision_for_unsafe ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1808+ fn restrict_precision_for_unsafe (
1809+ place : Place < ' tcx > ,
1810+ curr_mode : ty:: UpvarCapture < ' tcx > ,
1811+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
17681812 if place. projections . is_empty ( ) {
17691813 // Nothing to do here
1770- return place;
1814+ return ( place, curr_mode ) ;
17711815 }
17721816
17731817 if place. base_ty . is_unsafe_ptr ( ) {
1774- place. projections . truncate ( 0 ) ;
1775- return place;
1818+ return truncate_place_to_len ( place, curr_mode, 0 ) ;
17761819 }
17771820
17781821 if place. base_ty . is_union ( ) {
1779- place. projections . truncate ( 0 ) ;
1780- return place;
1822+ return truncate_place_to_len ( place, curr_mode, 0 ) ;
17811823 }
17821824
17831825 for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
17841826 if proj. ty . is_unsafe_ptr ( ) {
17851827 // Don't apply any projections on top of an unsafe ptr.
1786- place. projections . truncate ( i + 1 ) ;
1787- break ;
1828+ return truncate_place_to_len ( place, curr_mode, i + 1 ) ;
17881829 }
17891830
17901831 if proj. ty . is_union ( ) {
17911832 // Don't capture preicse fields of a union.
1792- place. projections . truncate ( i + 1 ) ;
1793- break ;
1833+ return truncate_place_to_len ( place, curr_mode, i + 1 ) ;
17941834 }
17951835 }
17961836
1797- place
1837+ ( place, curr_mode )
17981838}
17991839
18001840/// Truncate projections so that following rules are obeyed by the captured `place`:
18011841/// - No Index projections are captured, since arrays are captured completely.
18021842/// - No unsafe block is required to capture `place`
1803- /// Truncate projections so that following rules are obeyed by the captured `place`:
1804- fn restrict_capture_precision < ' tcx > ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1805- place = restrict_precision_for_unsafe ( place) ;
1843+ /// Returns the truncated place and updated cature mode.
1844+ fn restrict_capture_precision < ' tcx > (
1845+ place : Place < ' tcx > ,
1846+ curr_mode : ty:: UpvarCapture < ' tcx > ,
1847+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
1848+ let ( place, curr_mode) = restrict_precision_for_unsafe ( place, curr_mode) ;
18061849
18071850 if place. projections . is_empty ( ) {
18081851 // Nothing to do here
1809- return place;
1852+ return ( place, curr_mode ) ;
18101853 }
18111854
18121855 for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
18131856 match proj. kind {
18141857 ProjectionKind :: Index => {
18151858 // Arrays are completely captured, so we drop Index projections
1816- place. projections . truncate ( i) ;
1817- break ;
1859+ return truncate_place_to_len ( place, curr_mode, i) ;
18181860 }
18191861 ProjectionKind :: Deref => { }
18201862 ProjectionKind :: Field ( ..) => { } // ignore
18211863 ProjectionKind :: Subslice => { } // We never capture this
18221864 }
18231865 }
18241866
1825- place
1867+ return ( place, curr_mode ) ;
18261868}
18271869
18281870/// Take ownership if data being accessed is owned by the variable used to access it
18291871/// (or if closure attempts to move data that it doesn’t own).
18301872/// Note: When taking ownership, only capture data found on the stack.
18311873fn adjust_for_move_closure < ' tcx > (
1832- mut place : Place < ' tcx > ,
1874+ place : Place < ' tcx > ,
18331875 kind : ty:: UpvarCapture < ' tcx > ,
18341876) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
18351877 let contains_deref_of_ref = place. deref_tys ( ) . any ( |ty| ty. is_ref ( ) ) ;
@@ -1843,7 +1885,7 @@ fn adjust_for_move_closure<'tcx>(
18431885 _ if first_deref. is_some ( ) => {
18441886 let place = match first_deref {
18451887 Some ( idx) => {
1846- place. projections . truncate ( idx) ;
1888+ let ( place, _ ) = truncate_place_to_len ( place , kind , idx) ;
18471889 place
18481890 }
18491891 None => place,
@@ -1861,8 +1903,8 @@ fn adjust_for_move_closure<'tcx>(
18611903/// Adjust closure capture just that if taking ownership of data, only move data
18621904/// from enclosing stack frame.
18631905fn adjust_for_non_move_closure < ' tcx > (
1864- mut place : Place < ' tcx > ,
1865- kind : ty:: UpvarCapture < ' tcx > ,
1906+ place : Place < ' tcx > ,
1907+ mut kind : ty:: UpvarCapture < ' tcx > ,
18661908) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
18671909 let contains_deref =
18681910 place. projections . iter ( ) . position ( |proj| proj. kind == ProjectionKind :: Deref ) ;
@@ -1871,7 +1913,9 @@ fn adjust_for_non_move_closure<'tcx>(
18711913 ty:: UpvarCapture :: ByValue ( ..) if contains_deref. is_some ( ) => {
18721914 let place = match contains_deref {
18731915 Some ( idx) => {
1874- place. projections . truncate ( idx) ;
1916+ let ( place, new_kind) = truncate_place_to_len ( place, kind, idx) ;
1917+
1918+ kind = new_kind;
18751919 place
18761920 }
18771921 // Because of the if guard on the match on `kind`, we should never get here.
@@ -2072,6 +2116,49 @@ fn determine_capture_info(
20722116 }
20732117}
20742118
2119+ /// Truncates `place` to have up to `len` projections.
2120+ /// `curr_mode` is the current required capture kind for the place.
2121+ /// Returns the truncated `place` and the updated required capture kind.
2122+ ///
2123+ /// Note: Capture kind changes from `MutBorrow` to `UniqueImmBorrow` if the truncated part of the `place`
2124+ /// contained `Deref` of `&mut`.
2125+ fn truncate_place_to_len (
2126+ mut place : Place < ' tcx > ,
2127+ curr_mode : ty:: UpvarCapture < ' tcx > ,
2128+ len : usize ,
2129+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
2130+ let is_mut_ref = |ty : Ty < ' _ > | matches ! ( ty. kind( ) , ty:: Ref ( .., hir:: Mutability :: Mut ) ) ;
2131+
2132+ let mut capture_kind = curr_mode;
2133+
2134+ // If the truncated part of the place contains `Deref` of a `&mut` then convert MutBorrow ->
2135+ // UniqueImmBorrow
2136+ // Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so
2137+ // we don't need to worry about that case here.
2138+ match curr_mode {
2139+ ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow { kind : ty:: BorrowKind :: MutBorrow , region } ) => {
2140+ for i in len..place. projections . len ( ) {
2141+ if place. projections [ i] . kind == ProjectionKind :: Deref
2142+ && is_mut_ref ( place. ty_before_projection ( i) )
2143+ {
2144+ capture_kind = ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow {
2145+ kind : ty:: BorrowKind :: UniqueImmBorrow ,
2146+ region,
2147+ } ) ;
2148+ break ;
2149+ }
2150+ }
2151+ }
2152+
2153+ ty:: UpvarCapture :: ByRef ( ..) => { }
2154+ ty:: UpvarCapture :: ByValue ( ..) => { }
2155+ }
2156+
2157+ place. projections . truncate ( len) ;
2158+
2159+ ( place, capture_kind)
2160+ }
2161+
20752162/// Determines the Ancestry relationship of Place A relative to Place B
20762163///
20772164/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B
@@ -2133,7 +2220,10 @@ fn determine_place_ancestry_relation(
21332220/// // it is constrained to `'a`
21342221/// }
21352222/// ```
2136- fn truncate_capture_for_optimization < ' tcx > ( place : & Place < ' tcx > ) -> Place < ' tcx > {
2223+ fn truncate_capture_for_optimization < ' tcx > (
2224+ place : Place < ' tcx > ,
2225+ curr_mode : ty:: UpvarCapture < ' tcx > ,
2226+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
21372227 let is_shared_ref = |ty : Ty < ' _ > | matches ! ( ty. kind( ) , ty:: Ref ( .., hir:: Mutability :: Not ) ) ;
21382228
21392229 // Find the right-most deref (if any). All the projections that come after this
@@ -2144,9 +2234,9 @@ fn truncate_capture_for_optimization<'tcx>(place: &Place<'tcx>) -> Place<'tcx> {
21442234 match idx {
21452235 // If that pointer is a shared reference, then we don't need those fields.
21462236 Some ( idx) if is_shared_ref ( place. ty_before_projection ( idx) ) => {
2147- Place { projections : place . projections [ 0 ..= idx] . to_vec ( ) , ..place . clone ( ) }
2237+ truncate_place_to_len ( place , curr_mode , idx + 1 )
21482238 }
2149- None | Some ( _) => place . clone ( ) ,
2239+ None | Some ( _) => ( place , curr_mode ) ,
21502240 }
21512241}
21522242
0 commit comments