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
5 changes: 5 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, ?next_point, "region isn't live at successor");
return None;
}

Expand All @@ -271,6 +273,8 @@ fn compute_forward_successor(
let direction =
live_region_variances.get(&region).unwrap_or(&ConstraintDirection::Bidirectional);

debug!(?direction);

match direction {
ConstraintDirection::Backward => {
// Contravariant cases: loans flow in the inverse direction, but we're only interested
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/polonius/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
});
Expand Down
38 changes: 23 additions & 15 deletions compiler/rustc_borrowck/src/type_check/liveness/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<PointIndex>) {
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`
Expand Down Expand Up @@ -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 {
Expand All @@ -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<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
value: GenericArg<'tcx>,
live_at: &IntervalSet<PointIndex>,
) {
debug!("make_all_regions_live(value={:?})", value);
Expand All @@ -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);
}
}

Expand Down
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: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`.
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: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`.
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: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`.
Original file line number Diff line number Diff line change
@@ -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: 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
}
Loading