Skip to content

Commit 790309b

Browse files
committed
Auto merge of rust-lang#115315 - RalfJung:field-capture-packed-alignment, r=oli-obk
closure field capturing: don't depend on alignment of packed fields This fixes the closure field capture part of rust-lang#115305: field capturing always stops at projections into packed structs, no matter the alignment of the field. This means changing a private field type from `u8` to `u64` can never change how closures capture fields, which is probably what we want. Here's an example where, before this PR, changing the type of a private field in a repr(Rust) struct can change the output of a program: ```rust #![allow(dead_code)] mod m { // before patch #[derive(Default)] pub struct S1(u8); // after patch #[derive(Default)] pub struct S2(u64); } struct NoisyDrop; impl Drop for NoisyDrop { fn drop(&mut self) { eprintln!("dropped!"); } } #[repr(packed)] struct MyType { field: m::S1, // output changes when this becomes S2 other_field: NoisyDrop, third_field: Vec<()>, } fn test(r: MyType) { let c = || { let _val = std::ptr::addr_of!(r.field); let _val = r.third_field; }; drop(c); eprintln!("before dropping"); } fn main() { test(MyType { field: Default::default(), other_field: NoisyDrop, third_field: Vec::new(), }); } ``` Of course this is a breaking change for the same reason that doing field capturing in the first place was a breaking change. Packed fields are relatively rare and depending on drop order is relatively rare, so I don't expect this to have much impact, but it's hard to be sure and even a crater run will only tell us so much. Also see the [nomination comment](rust-lang#115315 (comment)). Cc `@rust-lang/wg-rfc-2229` `@ehuss`
2 parents 635c4a5 + a671127 commit 790309b

File tree

3 files changed

+22
-53
lines changed

3 files changed

+22
-53
lines changed

compiler/rustc_hir_typeck/src/upvar.rs

+10-36
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
195195

196196
assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id);
197197
let mut delegate = InferBorrowKind {
198-
fcx: self,
199198
closure_def_id,
200199
capture_information: Default::default(),
201200
fake_reads: Default::default(),
@@ -1607,34 +1606,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16071606
/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240,
16081607
/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
16091608
fn restrict_repr_packed_field_ref_capture<'tcx>(
1610-
tcx: TyCtxt<'tcx>,
1611-
param_env: ty::ParamEnv<'tcx>,
16121609
mut place: Place<'tcx>,
16131610
mut curr_borrow_kind: ty::UpvarCapture,
16141611
) -> (Place<'tcx>, ty::UpvarCapture) {
16151612
let pos = place.projections.iter().enumerate().position(|(i, p)| {
16161613
let ty = place.ty_before_projection(i);
16171614

1618-
// Return true for fields of packed structs, unless those fields have alignment 1.
1615+
// Return true for fields of packed structs.
16191616
match p.kind {
16201617
ProjectionKind::Field(..) => match ty.kind() {
16211618
ty::Adt(def, _) if def.repr().packed() => {
1622-
// We erase regions here because they cannot be hashed
1623-
match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) {
1624-
Ok(layout) if layout.align.abi.bytes() == 1 => {
1625-
// if the alignment is 1, the type can't be further
1626-
// disaligned.
1627-
debug!(
1628-
"restrict_repr_packed_field_ref_capture: ({:?}) - align = 1",
1629-
place
1630-
);
1631-
false
1632-
}
1633-
_ => {
1634-
debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place);
1635-
true
1636-
}
1637-
}
1619+
// We stop here regardless of field alignment. Field alignment can change as
1620+
// types change, including the types of private fields in other crates, and that
1621+
// shouldn't affect how we compute our captures.
1622+
true
16381623
}
16391624

16401625
_ => false,
@@ -1689,9 +1674,7 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span {
16891674
tcx.sess.source_map().end_point(owner_span)
16901675
}
16911676

1692-
struct InferBorrowKind<'a, 'tcx> {
1693-
fcx: &'a FnCtxt<'a, 'tcx>,
1694-
1677+
struct InferBorrowKind<'tcx> {
16951678
// The def-id of the closure whose kind and upvar accesses are being inferred.
16961679
closure_def_id: LocalDefId,
16971680

@@ -1725,7 +1708,7 @@ struct InferBorrowKind<'a, 'tcx> {
17251708
fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>,
17261709
}
17271710

1728-
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
1711+
impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> {
17291712
fn fake_read(
17301713
&mut self,
17311714
place: &PlaceWithHirId<'tcx>,
@@ -1740,12 +1723,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17401723

17411724
let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind);
17421725

1743-
let (place, _) = restrict_repr_packed_field_ref_capture(
1744-
self.fcx.tcx,
1745-
self.fcx.param_env,
1746-
place,
1747-
dummy_capture_kind,
1748-
);
1726+
let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind);
17491727
self.fake_reads.push((place, cause, diag_expr_id));
17501728
}
17511729

@@ -1780,12 +1758,8 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
17801758
// We only want repr packed restriction to be applied to reading references into a packed
17811759
// struct, and not when the data is being moved. Therefore we call this method here instead
17821760
// of in `restrict_capture_precision`.
1783-
let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture(
1784-
self.fcx.tcx,
1785-
self.fcx.param_env,
1786-
place_with_id.place.clone(),
1787-
capture_kind,
1788-
);
1761+
let (place, mut capture_kind) =
1762+
restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind);
17891763

17901764
// Raw pointers don't inherit mutability
17911765
if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) {

tests/ui/closures/2229_closure_analysis/repr_packed.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
#![feature(rustc_attrs)]
44

55
// `u8` aligned at a byte and are unaffected by repr(packed).
6-
// Therefore we can precisely (and safely) capture references to both the fields.
6+
// Therefore we *could* precisely (and safely) capture references to both the fields,
7+
// but we don't, since we don't want capturing to change when field types change alignment.
78
fn test_alignment_not_affected() {
89
#[repr(packed)]
910
struct Foo { x: u8, y: u8 }
@@ -17,11 +18,10 @@ fn test_alignment_not_affected() {
1718
//~^ ERROR: First Pass analysis includes:
1819
//~| ERROR: Min Capture analysis includes:
1920
let z1: &u8 = &foo.x;
20-
//~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow
21-
//~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow
21+
//~^ NOTE: Capturing foo[] -> ImmBorrow
2222
let z2: &mut u8 = &mut foo.y;
23-
//~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow
24-
//~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow
23+
//~^ NOTE: Capturing foo[] -> MutBorrow
24+
//~| NOTE: Min Capture foo[] -> MutBorrow
2525

2626
*z2 = 42;
2727

tests/ui/closures/2229_closure_analysis/repr_packed.stderr

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0658]: attributes on expressions are experimental
2-
--> $DIR/repr_packed.rs:13:17
2+
--> $DIR/repr_packed.rs:14:17
33
|
44
LL | let mut c = #[rustc_capture_analysis]
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL | let c = #[rustc_capture_analysis]
2626
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
2727

2828
error: First Pass analysis includes:
29-
--> $DIR/repr_packed.rs:16:5
29+
--> $DIR/repr_packed.rs:17:5
3030
|
3131
LL | / || {
3232
LL | |
@@ -37,19 +37,19 @@ LL | | println!("({}, {})", z1, z2);
3737
LL | | };
3838
| |_____^
3939
|
40-
note: Capturing foo[(0, 0)] -> ImmBorrow
41-
--> $DIR/repr_packed.rs:19:24
40+
note: Capturing foo[] -> ImmBorrow
41+
--> $DIR/repr_packed.rs:20:24
4242
|
4343
LL | let z1: &u8 = &foo.x;
4444
| ^^^^^
45-
note: Capturing foo[(1, 0)] -> MutBorrow
45+
note: Capturing foo[] -> MutBorrow
4646
--> $DIR/repr_packed.rs:22:32
4747
|
4848
LL | let z2: &mut u8 = &mut foo.y;
4949
| ^^^^^
5050

5151
error: Min Capture analysis includes:
52-
--> $DIR/repr_packed.rs:16:5
52+
--> $DIR/repr_packed.rs:17:5
5353
|
5454
LL | / || {
5555
LL | |
@@ -60,12 +60,7 @@ LL | | println!("({}, {})", z1, z2);
6060
LL | | };
6161
| |_____^
6262
|
63-
note: Min Capture foo[(0, 0)] -> ImmBorrow
64-
--> $DIR/repr_packed.rs:19:24
65-
|
66-
LL | let z1: &u8 = &foo.x;
67-
| ^^^^^
68-
note: Min Capture foo[(1, 0)] -> MutBorrow
63+
note: Min Capture foo[] -> MutBorrow
6964
--> $DIR/repr_packed.rs:22:32
7065
|
7166
LL | let z2: &mut u8 = &mut foo.y;

0 commit comments

Comments
 (0)