@@ -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;
@@ -1447,7 +1476,8 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
14471476 tcx : TyCtxt < ' tcx > ,
14481477 param_env : ty:: ParamEnv < ' tcx > ,
14491478 place : & Place < ' tcx > ,
1450- ) -> Place < ' tcx > {
1479+ curr_borrow_kind : ty:: UpvarCapture < ' tcx > ,
1480+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
14511481 let pos = place. projections . iter ( ) . enumerate ( ) . position ( |( i, p) | {
14521482 let ty = place. ty_before_projection ( i) ;
14531483
@@ -1478,13 +1508,13 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
14781508 }
14791509 } ) ;
14801510
1481- let mut place = place. clone ( ) ;
1511+ let place = place. clone ( ) ;
14821512
14831513 if let Some ( pos) = pos {
1484- place. projections . truncate ( pos) ;
1514+ truncate_place_to_len ( place, curr_borrow_kind, pos)
1515+ } else {
1516+ ( place, curr_borrow_kind)
14851517 }
1486-
1487- place
14881518}
14891519
14901520/// Returns a Ty that applies the specified capture kind on the provided capture Ty
@@ -1605,20 +1635,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
16051635 ) ;
16061636
16071637 if let PlaceBase :: Upvar ( _) = place_with_id. place . base {
1608- let mut borrow_kind = ty:: MutBorrow ;
1609- for pointer_ty in place_with_id. place . deref_tys ( ) {
1610- match pointer_ty. kind ( ) {
1611- // Raw pointers don't inherit mutability.
1612- ty:: RawPtr ( _) => return ,
1613- // assignment to deref of an `&mut`
1614- // borrowed pointer implies that the
1615- // pointer itself must be unique, but not
1616- // necessarily *mutable*
1617- ty:: Ref ( .., hir:: Mutability :: Mut ) => borrow_kind = ty:: UniqueImmBorrow ,
1618- _ => ( ) ,
1619- }
1638+ // Raw pointers don't inherit mutability
1639+ if place_with_id. place . deref_tys ( ) . any ( ty:: TyS :: is_unsafe_ptr) {
1640+ return ;
16201641 }
1621- self . adjust_upvar_deref ( place_with_id, diag_expr_id, borrow_kind ) ;
1642+ self . adjust_upvar_deref ( place_with_id, diag_expr_id, ty :: MutBorrow ) ;
16221643 }
16231644 }
16241645
@@ -1735,9 +1756,19 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17351756 if let PlaceBase :: Upvar ( _) = place. base {
17361757 // We need to restrict Fake Read precision to avoid fake reading unsafe code,
17371758 // such as deref of a raw pointer.
1738- let place = restrict_capture_precision ( place) ;
1739- let place =
1740- restrict_repr_packed_field_ref_capture ( self . fcx . tcx , self . fcx . param_env , & place) ;
1759+ let dummy_capture_kind = ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow {
1760+ kind : ty:: BorrowKind :: ImmBorrow ,
1761+ region : & ty:: ReErased ,
1762+ } ) ;
1763+
1764+ let ( place, _) = restrict_capture_precision ( place, dummy_capture_kind) ;
1765+
1766+ let ( place, _) = restrict_repr_packed_field_ref_capture (
1767+ self . fcx . tcx ,
1768+ self . fcx . param_env ,
1769+ & place,
1770+ dummy_capture_kind,
1771+ ) ;
17411772 self . fake_reads . push ( ( place, cause, diag_expr_id) ) ;
17421773 }
17431774 }
@@ -1763,13 +1794,18 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17631794 place_with_id, diag_expr_id, bk
17641795 ) ;
17651796
1797+ // The region here will get discarded/ignored
1798+ let dummy_capture_kind =
1799+ ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow { kind : bk, region : & ty:: ReErased } ) ;
1800+
17661801 // We only want repr packed restriction to be applied to reading references into a packed
17671802 // struct, and not when the data is being moved. Therefore we call this method here instead
17681803 // of in `restrict_capture_precision`.
1769- let place = restrict_repr_packed_field_ref_capture (
1804+ let ( place, updated_kind ) = restrict_repr_packed_field_ref_capture (
17701805 self . fcx . tcx ,
17711806 self . fcx . param_env ,
17721807 & place_with_id. place ,
1808+ dummy_capture_kind,
17731809 ) ;
17741810
17751811 let place_with_id = PlaceWithHirId { place, ..* place_with_id } ;
@@ -1778,14 +1814,19 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17781814 self . init_capture_info_for_place ( & place_with_id, diag_expr_id) ;
17791815 }
17801816
1781- match bk {
1782- ty:: ImmBorrow => { }
1783- ty:: UniqueImmBorrow => {
1784- self . adjust_upvar_borrow_kind_for_unique ( & place_with_id, diag_expr_id) ;
1785- }
1786- ty:: MutBorrow => {
1787- self . adjust_upvar_borrow_kind_for_mut ( & place_with_id, diag_expr_id) ;
1788- }
1817+ match updated_kind {
1818+ ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow { kind, .. } ) => match kind {
1819+ ty:: ImmBorrow => { }
1820+ ty:: UniqueImmBorrow => {
1821+ self . adjust_upvar_borrow_kind_for_unique ( & place_with_id, diag_expr_id) ;
1822+ }
1823+ ty:: MutBorrow => {
1824+ self . adjust_upvar_borrow_kind_for_mut ( & place_with_id, diag_expr_id) ;
1825+ }
1826+ } ,
1827+
1828+ // Just truncating the place will never cause capture kind to be updated to ByValue
1829+ ty:: UpvarCapture :: ByValue ( ..) => unreachable ! ( ) ,
17891830 }
17901831 }
17911832
@@ -1799,72 +1840,73 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17991840/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
18001841/// them completely.
18011842/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
1802- fn restrict_precision_for_unsafe ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1843+ fn restrict_precision_for_unsafe (
1844+ place : Place < ' tcx > ,
1845+ curr_mode : ty:: UpvarCapture < ' tcx > ,
1846+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
18031847 if place. projections . is_empty ( ) {
18041848 // Nothing to do here
1805- return place;
1849+ return ( place, curr_mode ) ;
18061850 }
18071851
18081852 if place. base_ty . is_unsafe_ptr ( ) {
1809- place. projections . truncate ( 0 ) ;
1810- return place;
1853+ return truncate_place_to_len ( place, curr_mode, 0 ) ;
18111854 }
18121855
18131856 if place. base_ty . is_union ( ) {
1814- place. projections . truncate ( 0 ) ;
1815- return place;
1857+ return truncate_place_to_len ( place, curr_mode, 0 ) ;
18161858 }
18171859
18181860 for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
18191861 if proj. ty . is_unsafe_ptr ( ) {
18201862 // Don't apply any projections on top of an unsafe ptr.
1821- place. projections . truncate ( i + 1 ) ;
1822- break ;
1863+ return truncate_place_to_len ( place, curr_mode, i + 1 ) ;
18231864 }
18241865
18251866 if proj. ty . is_union ( ) {
18261867 // Don't capture preicse fields of a union.
1827- place. projections . truncate ( i + 1 ) ;
1828- break ;
1868+ return truncate_place_to_len ( place, curr_mode, i + 1 ) ;
18291869 }
18301870 }
18311871
1832- place
1872+ ( place, curr_mode )
18331873}
18341874
18351875/// Truncate projections so that following rules are obeyed by the captured `place`:
18361876/// - No Index projections are captured, since arrays are captured completely.
18371877/// - No unsafe block is required to capture `place`
1838- /// Truncate projections so that following rules are obeyed by the captured `place`:
1839- fn restrict_capture_precision < ' tcx > ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1840- place = restrict_precision_for_unsafe ( place) ;
1878+ /// Returns the truncated place and updated cature mode.
1879+ fn restrict_capture_precision < ' tcx > (
1880+ place : Place < ' tcx > ,
1881+ curr_mode : ty:: UpvarCapture < ' tcx > ,
1882+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
1883+ let ( place, curr_mode) = restrict_precision_for_unsafe ( place, curr_mode) ;
18411884
18421885 if place. projections . is_empty ( ) {
18431886 // Nothing to do here
1844- return place;
1887+ return ( place, curr_mode ) ;
18451888 }
18461889
18471890 for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
18481891 match proj. kind {
18491892 ProjectionKind :: Index => {
18501893 // Arrays are completely captured, so we drop Index projections
1851- place. projections . truncate ( i) ;
1852- break ;
1894+ return truncate_place_to_len ( place, curr_mode, i) ;
18531895 }
18541896 ProjectionKind :: Deref => { }
18551897 ProjectionKind :: Field ( ..) => { } // ignore
18561898 ProjectionKind :: Subslice => { } // We never capture this
18571899 }
18581900 }
18591901
1860- place
1902+ return ( place, curr_mode ) ;
18611903}
18621904
18631905/// Take ownership if data being accessed is owned by the variable used to access it
18641906/// (or if closure attempts to move data that it doesn’t own).
18651907/// Note: When taking ownership, only capture data found on the stack.
18661908fn adjust_for_move_closure < ' tcx > (
1867- mut place : Place < ' tcx > ,
1909+ place : Place < ' tcx > ,
18681910 kind : ty:: UpvarCapture < ' tcx > ,
18691911) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
18701912 let contains_deref_of_ref = place. deref_tys ( ) . any ( |ty| ty. is_ref ( ) ) ;
@@ -1878,7 +1920,7 @@ fn adjust_for_move_closure<'tcx>(
18781920 _ if first_deref. is_some ( ) => {
18791921 let place = match first_deref {
18801922 Some ( idx) => {
1881- place. projections . truncate ( idx) ;
1923+ let ( place, _ ) = truncate_place_to_len ( place , kind , idx) ;
18821924 place
18831925 }
18841926 None => place,
@@ -1896,8 +1938,8 @@ fn adjust_for_move_closure<'tcx>(
18961938/// Adjust closure capture just that if taking ownership of data, only move data
18971939/// from enclosing stack frame.
18981940fn adjust_for_non_move_closure < ' tcx > (
1899- mut place : Place < ' tcx > ,
1900- kind : ty:: UpvarCapture < ' tcx > ,
1941+ place : Place < ' tcx > ,
1942+ mut kind : ty:: UpvarCapture < ' tcx > ,
19011943) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
19021944 let contains_deref =
19031945 place. projections . iter ( ) . position ( |proj| proj. kind == ProjectionKind :: Deref ) ;
@@ -1906,7 +1948,9 @@ fn adjust_for_non_move_closure<'tcx>(
19061948 ty:: UpvarCapture :: ByValue ( ..) if contains_deref. is_some ( ) => {
19071949 let place = match contains_deref {
19081950 Some ( idx) => {
1909- place. projections . truncate ( idx) ;
1951+ let ( place, new_kind) = truncate_place_to_len ( place, kind, idx) ;
1952+
1953+ kind = new_kind;
19101954 place
19111955 }
19121956 // Because of the if guard on the match on `kind`, we should never get here.
@@ -2107,6 +2151,49 @@ fn determine_capture_info(
21072151 }
21082152}
21092153
2154+ /// Truncates `place` to have up to `len` projections.
2155+ /// `curr_mode` is the current required capture kind for the place.
2156+ /// Returns the truncated `place` and the updated required capture kind.
2157+ ///
2158+ /// Note: Capture kind changes from `MutBorrow` to `UniqueImmBorrow` if the truncated part of the `place`
2159+ /// contained `Deref` of `&mut`.
2160+ fn truncate_place_to_len (
2161+ mut place : Place < ' tcx > ,
2162+ curr_mode : ty:: UpvarCapture < ' tcx > ,
2163+ len : usize ,
2164+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
2165+ let is_mut_ref = |ty : Ty < ' _ > | matches ! ( ty. kind( ) , ty:: Ref ( .., hir:: Mutability :: Mut ) ) ;
2166+
2167+ let mut capture_kind = curr_mode;
2168+
2169+ // If the truncated part of the place contains `Deref` of a `&mut` then convert MutBorrow ->
2170+ // UniqueImmBorrow
2171+ // Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so
2172+ // we don't need to worry about that case here.
2173+ match curr_mode {
2174+ ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow { kind : ty:: BorrowKind :: MutBorrow , region } ) => {
2175+ for i in len..place. projections . len ( ) {
2176+ if place. projections [ i] . kind == ProjectionKind :: Deref
2177+ && is_mut_ref ( place. ty_before_projection ( i) )
2178+ {
2179+ capture_kind = ty:: UpvarCapture :: ByRef ( ty:: UpvarBorrow {
2180+ kind : ty:: BorrowKind :: UniqueImmBorrow ,
2181+ region,
2182+ } ) ;
2183+ break ;
2184+ }
2185+ }
2186+ }
2187+
2188+ ty:: UpvarCapture :: ByRef ( ..) => { }
2189+ ty:: UpvarCapture :: ByValue ( ..) => { }
2190+ }
2191+
2192+ place. projections . truncate ( len) ;
2193+
2194+ ( place, capture_kind)
2195+ }
2196+
21102197/// Determines the Ancestry relationship of Place A relative to Place B
21112198///
21122199/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B
@@ -2168,7 +2255,10 @@ fn determine_place_ancestry_relation(
21682255/// // it is constrained to `'a`
21692256/// }
21702257/// ```
2171- fn truncate_capture_for_optimization < ' tcx > ( place : & Place < ' tcx > ) -> Place < ' tcx > {
2258+ fn truncate_capture_for_optimization < ' tcx > (
2259+ place : Place < ' tcx > ,
2260+ curr_mode : ty:: UpvarCapture < ' tcx > ,
2261+ ) -> ( Place < ' tcx > , ty:: UpvarCapture < ' tcx > ) {
21722262 let is_shared_ref = |ty : Ty < ' _ > | matches ! ( ty. kind( ) , ty:: Ref ( .., hir:: Mutability :: Not ) ) ;
21732263
21742264 // Find the right-most deref (if any). All the projections that come after this
@@ -2179,9 +2269,9 @@ fn truncate_capture_for_optimization<'tcx>(place: &Place<'tcx>) -> Place<'tcx> {
21792269 match idx {
21802270 // If that pointer is a shared reference, then we don't need those fields.
21812271 Some ( idx) if is_shared_ref ( place. ty_before_projection ( idx) ) => {
2182- Place { projections : place . projections [ 0 ..= idx] . to_vec ( ) , ..place . clone ( ) }
2272+ truncate_place_to_len ( place , curr_mode , idx + 1 )
21832273 }
2184- None | Some ( _) => place . clone ( ) ,
2274+ None | Some ( _) => ( place , curr_mode ) ,
21852275 }
21862276}
21872277
0 commit comments