Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_borrowck/src/polonius/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 => {
Expand All @@ -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;
}

Expand Down
34 changes: 23 additions & 11 deletions compiler/rustc_borrowck/src/type_check/liveness/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PointIndex>) {
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);
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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<TyCtxt<'tcx>> + Relate<TyCtxt<'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>,
Expand All @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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: HasArg>(T::Arg);

trait HasArg {
type Arg;
}
impl<'a, T> HasArg for fn(&'a T) {
type Arg = &'a T;
}
impl<T: HasArg> Drop for D<T> {
fn drop(&mut self) {}
}
fn mk<'a, T>(r: &'a T) -> D<fn(&'a T)> {
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
}
Original file line number Diff line number Diff line change
@@ -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`.
Loading