diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 39874f48eb014..013cb2a49b29c 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -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; @@ -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 => {} @@ -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 } diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87378.rs b/src/test/ui/closures/2229_closure_analysis/issue-87378.rs new file mode 100644 index 0000000000000..75901a5718bae --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87378.rs @@ -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 + || { + //~^ 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(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr b/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr new file mode 100644 index 0000000000000..16c3f7c976dd7 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/issue-87378.stderr @@ -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 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`. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs new file mode 100644 index 0000000000000..c64475fda43de --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs @@ -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(); +}