From 8265b6f02badba2c390373237f3766d2a2b0651f Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 16 Sep 2026 15:13:34 +0000 Subject: [PATCH] Use the entire type of the live variable to compute region variance Co-authored-by: Amanda Stjerna Co-authored-by: Jack Huey <31162821+jackh726@users.noreply.github.com> --- .../src/polonius/constraints.rs | 5 +++ .../rustc_borrowck/src/polonius/legacy/mod.rs | 2 +- .../src/type_check/liveness/trace.rs | 38 +++++++++++------- ...ness-invariance-issue-160670.legacy.stderr | 22 +++++++++++ ...iveness-invariance-issue-160670.nll.stderr | 22 +++++++++++ ...ss-invariance-issue-160670.polonius.stderr | 22 +++++++++++ .../drop-liveness-invariance-issue-160670.rs | 39 +++++++++++++++++++ 7 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.legacy.stderr create mode 100644 tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr create mode 100644 tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr create mode 100644 tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 2808cbee99fb0..138bd6a6a813a 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, ?next_point, "region isn't live at successor"); return None; } @@ -271,6 +273,8 @@ fn compute_forward_successor( let direction = live_region_variances.get(®ion).unwrap_or(&ConstraintDirection::Bidirectional); + debug!(?direction); + match direction { ConstraintDirection::Backward => { // Contravariant cases: loans flow in the inverse direction, but we're only interested @@ -299,6 +303,7 @@ 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!(?region, ?current_point, "region isn't live at current point"); return None; } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 0ae3ff3c04790..1aae724266c6f 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -192,7 +192,7 @@ pub(crate) fn emit_drop_facts<'tcx>( debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); let Some(facts) = facts.as_mut() else { return }; let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - tcx.for_each_free_region(kind, |drop_live_region| { + tcx.for_each_free_region(&kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); facts.drop_of_var_derefs_origin.push((local, region_vid.into())); }); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 89a8899a991c9..6ca2ddca7457b 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -5,8 +5,7 @@ use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; -use rustc_middle::ty::relate::Relate; -use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{GenericArg, Ty, TypeVisitable, TypeVisitableExt}; use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; @@ -532,7 +531,8 @@ 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::make_all_regions_live(self.location_map, self.typeck, value, live_at); + Self::record_region_variance(self.typeck, value.into()); + Self::make_all_regions_live(self.location_map, self.typeck, value.into(), live_at); } /// Some variable with type `live_ty` is "drop live" at `location` @@ -573,6 +573,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.into()); + // 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,10 +590,25 @@ 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: GenericArg<'tcx>) { + // 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>, - value: impl TypeVisitable> + Relate>, + value: GenericArg<'tcx>, live_at: &IntervalSet, ) { debug!("make_all_regions_live(value={:?})", value); @@ -604,20 +622,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.legacy.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.legacy.stderr new file mode 100644 index 0000000000000..8cf41b45af61d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.legacy.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:38: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`. diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr new file mode 100644 index 0000000000000..8cf41b45af61d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.nll.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:38: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`. diff --git a/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr new file mode 100644 index 0000000000000..8cf41b45af61d --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.polonius.stderr @@ -0,0 +1,22 @@ +error[E0505]: cannot move out of `b` because it is borrowed + --> $DIR/drop-liveness-invariance-issue-160670.rs:38: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`. 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..b1f53190b2c74 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/drop-liveness-invariance-issue-160670.rs @@ -0,0 +1,39 @@ +// 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 39--40 is load bearing; without +// them the bug does not appear due to a `FakeRead` being introduced and +// ensuring liveness. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [nll] compile-flags: -Z polonius=off +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy + +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 +}