diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 2808cbee99fb0..3ac83f4078b00 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -5,6 +5,7 @@ use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::RegionVid; use rustc_mir_dataflow::points::PointIndex; +use tracing::debug; use crate::BorrowSet; use crate::constraints::OutlivesConstraint; @@ -255,6 +256,7 @@ fn compute_forward_successor( // 2. Otherwise, gather the edges due to explicit region liveness, when applicable. if !live_regions.contains(region, next_point) { + debug!("Region {region:?} isn't live at successor {next_point:?}; traversal stops."); return None; } @@ -275,6 +277,7 @@ fn compute_forward_successor( ConstraintDirection::Backward => { // Contravariant cases: loans flow in the inverse direction, but we're only interested // in forward successors and there are none here. + debug!("Constraint direction is backwards; {region:?} has no forward successors"); None } ConstraintDirection::Forward | ConstraintDirection::Bidirectional => { @@ -299,6 +302,9 @@ fn compute_backward_successor( // Liveness flows into the regions live at the next point. So, in a backwards view, we'll link // the region from the current point, if it's live there, to the previous point. if !live_regions.contains(region, current_point) { + debug!( + "Backwards successor: {region:?} not live at current point {current_point:?}; bailing out!" + ); return None; } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 89a8899a991c9..489e2eb303b4c 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -532,6 +532,7 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { /// points `live_at`. fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet) { debug!("add_use_live_facts_for(value={:?})", value); + Self::record_region_variance(self.typeck, value); Self::make_all_regions_live(self.location_map, self.typeck, value, live_at); } @@ -573,6 +574,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } + // Since the entire dropped local is live, record the variance of its regions. + Self::record_region_variance(self.typeck, dropped_ty); + // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { @@ -587,6 +591,24 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } + /// `live_kind` is the type of a (use- or drop-) live local. + /// Record the variance of any region(s) appearing in it for Polonius. Does + /// nothing if Polonius is not active. + fn record_region_variance( + typeck: &mut TypeChecker<'_, 'tcx>, + live_kind: impl TypeVisitable> + Relate>, + ) { + // When using `-Zpolonius=next`, we record the variance of each live region. + if let Some(polonius_context) = typeck.polonius_context.as_mut() { + record_live_region_variance( + typeck.infcx.tcx, + &mut polonius_context.live_region_variances, + typeck.universal_regions, + live_kind, + ); + } + } + fn make_all_regions_live( location_map: &DenseLocationMap, typeck: &mut TypeChecker<'_, 'tcx>, @@ -604,20 +626,10 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { param_env: typeck.infcx.param_env, op: |r| { let live_region_vid = typeck.universal_regions.to_region_vid(r); - typeck.constraints.liveness_constraints.add_points(live_region_vid, live_at); }, }); - - // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = typeck.polonius_context.as_mut() { - record_live_region_variance( - typeck.infcx.tcx, - &mut polonius_context.live_region_variances, - typeck.universal_regions, - value, - ); - } + Self::record_region_variance(typeck, value); } } diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs new file mode 100644 index 0000000000000..e08453598d4b6 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs @@ -0,0 +1,32 @@ +// From https://github.com/rust-lang/rust/issues/160670 This issue was +// discovered when developing Polonius alpha. A live local (the one holding the +// struct `D`) had its type be drop-live, but only partially. This had not +// previously triggered any issues because it did not affect region liveness, +// but it did affect Polonius' region variance computations, since the outer `D` +// nesting was removed to obtain `fn(&'a T)`, which unlike the associated type +// isn't invariant. +// +// The split declaration/assignment on lines 30--31 is load bearing; without +// them the bug does not appear. + +struct D(T::Arg); + +trait HasArg { + type Arg; +} +impl<'a, T> HasArg for fn(&'a T) { + type Arg = &'a T; +} +impl Drop for D { + fn drop(&mut self) {} +} +fn mk<'a, T>(r: &'a T) -> D { + D(r) +} + +fn main() { + let b = Box::new(0u8); + let d; + d = mk(&*b); + drop(b); //~ ERROR cannot move out of `b` because it is borrowed +} diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.stderr new file mode 100644 index 0000000000000..a24862f6f5190 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:31:10 + | +LL | let b = Box::new(0u8); + | - binding `b` declared here +LL | let d; +LL | d = mk(&*b); + | --- borrow of `*b` occurs here +LL | drop(b); + | ^ move out of `b` occurs here +LL | } + | - borrow might be used here, when `d` is dropped and runs the `Drop` code for type `D` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - d = mk(&*b); +LL + d = mk(&b.clone()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0505`.