Skip to content

Commit 8e89971

Browse files
committed
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
1 parent 8bebfe5 commit 8e89971

File tree

3 files changed

+166
-76
lines changed

3 files changed

+166
-76
lines changed

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;
@@ -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.
18311873
fn 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.
18631905
fn 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

Comments
 (0)