Skip to content

Commit 9583fd1

Browse files
committed
Auto merge of #87676 - sexxi-goose:truncate_unique, r=nikomatsakis
2229: Handle MutBorrow/UniqueImmBorrow better We only want to use UniqueImmBorrow when the capture place is truncated and we drop Deref of a MutRef. r? `@nikomatsakis` Fixes: rust-lang/project-rfc-2229#56
2 parents 52c881f + 8e89971 commit 9583fd1

File tree

3 files changed

+166
-76
lines changed

3 files changed

+166
-76
lines changed

Diff for: compiler/rustc_typeck/src/check/upvar.rs

+150-60
Original file line numberDiff line numberDiff line change
@@ -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.
18661908
fn 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.
18981940
fn 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

Comments
 (0)