Skip to content

Commit

Permalink
Auto merge of #87469 - sexxi-goose:union, r=nikomatsakis
Browse files Browse the repository at this point in the history
2229: Don't capture preicese paths on top of a union

- Accessing fields of a union require unsafe block
- As part of 2229 we don't allow precision where we need an unsafe block
to capture.

Fixes: #87378

r? `@nikomatsakis`
  • Loading branch information
bors committed Jul 26, 2021
2 parents fc24bce + 75edcd9 commit 08095fc
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 12 deletions.
46 changes: 34 additions & 12 deletions compiler/rustc_typeck/src/check/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1760,12 +1760,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow);
}
}

/// Truncate projections so that following rules are obeyed by the captured `place`:
/// Truncate `place` so that an `unsafe` block isn't required to capture it.
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
/// them completely.
/// - No Index projections are captured, since arrays are captured completely.
fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
fn restrict_precision_for_unsafe(mut place: Place<'tcx>) -> Place<'tcx> {
if place.projections.is_empty() {
// Nothing to do here
return place;
Expand All @@ -1776,18 +1775,45 @@ fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
return place;
}

let mut truncated_length = usize::MAX;
if place.base_ty.is_union() {
place.projections.truncate(0);
return place;
}

for (i, proj) in place.projections.iter().enumerate() {
if proj.ty.is_unsafe_ptr() {
// Don't apply any projections on top of an unsafe ptr
truncated_length = truncated_length.min(i + 1);
// Don't apply any projections on top of an unsafe ptr.
place.projections.truncate(i + 1);
break;
}

if proj.ty.is_union() {
// Don't capture preicse fields of a union.
place.projections.truncate(i + 1);
break;
}
}

place
}

/// Truncate projections so that following rules are obeyed by the captured `place`:
/// - No Index projections are captured, since arrays are captured completely.
/// - No unsafe block is required to capture `place`
/// Truncate projections so that following rules are obeyed by the captured `place`:
fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
place = restrict_precision_for_unsafe(place);

if place.projections.is_empty() {
// Nothing to do here
return place;
}

for (i, proj) in place.projections.iter().enumerate() {
match proj.kind {
ProjectionKind::Index => {
// Arrays are completely captured, so we drop Index projections
truncated_length = truncated_length.min(i);
place.projections.truncate(i);
break;
}
ProjectionKind::Deref => {}
Expand All @@ -1796,10 +1822,6 @@ fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
}
}

let length = place.projections.len().min(truncated_length);

place.projections.truncate(length);

place
}

Expand Down
26 changes: 26 additions & 0 deletions src/test/ui/closures/2229_closure_analysis/issue-87378.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![feature(rustc_attrs)]

// edition:2021

// Test that any precise capture on a union is truncated because it's unsafe to do so.

union Union {
value: u64,
}

fn main() {
let u = Union { value: 42 };

let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
|| {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
unsafe { u.value }
//~^ NOTE: Capturing u[(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture u[] -> ImmBorrow
};

c();
}
48 changes: 48 additions & 0 deletions src/test/ui/closures/2229_closure_analysis/issue-87378.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/issue-87378.rs:14:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable

error: First Pass analysis includes:
--> $DIR/issue-87378.rs:17:5
|
LL | / || {
LL | |
LL | |
LL | | unsafe { u.value }
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing u[(0, 0)] -> ImmBorrow
--> $DIR/issue-87378.rs:20:17
|
LL | unsafe { u.value }
| ^^^^^^^

error: Min Capture analysis includes:
--> $DIR/issue-87378.rs:17:5
|
LL | / || {
LL | |
LL | |
LL | | unsafe { u.value }
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture u[] -> ImmBorrow
--> $DIR/issue-87378.rs:20:17
|
LL | unsafe { u.value }
| ^^^^^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0658`.
16 changes: 16 additions & 0 deletions src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// edition:2021
// check-pass

union Union {
value: u64,
}

fn main() {
let u = Union { value: 42 };

let c = || {
unsafe { u.value }
};

c();
}

0 comments on commit 08095fc

Please sign in to comment.