Skip to content

Commit

Permalink
add variance recording
Browse files Browse the repository at this point in the history
Following the Generalizer's structure, relating e.g. a type with itself
will allow tracking the variance wrt the contained regions.
  • Loading branch information
lqd committed Dec 29, 2024
1 parent 42f28cb commit de049e6
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 0 deletions.
125 changes: 125 additions & 0 deletions compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::collections::BTreeMap;

use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};

use super::{ConstraintDirection, PoloniusContext};
use crate::universal_regions::UniversalRegions;

impl PoloniusContext {
/// Record the variance of each region contained within the given value.
pub(crate) fn record_live_region_variance<'tcx>(
&mut self,
tcx: TyCtxt<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
) {
let mut extractor = VarianceExtractor {
tcx,
ambient_variance: ty::Variance::Covariant,
directions: &mut self.live_region_variances,
universal_regions,
};
extractor.relate(value, value).expect("Can't have a type error relating to itself");
}
}

/// Extracts variances for regions contained within types. Follows the same structure as
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
/// variances of regions.
struct VarianceExtractor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
ambient_variance: ty::Variance,
directions: &'a mut BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &'a UniversalRegions<'tcx>,
}

impl<'tcx> VarianceExtractor<'_, 'tcx> {
fn record_variance(&mut self, region: ty::Region<'tcx>, variance: ty::Variance) {
// We're only interested in the variance of vars and free regions.

if region.is_bound() || region.is_erased() {
// ignore these
return;
}

let direction = match variance {
ty::Variance::Covariant => ConstraintDirection::Forward,
ty::Variance::Contravariant => ConstraintDirection::Backward,
ty::Variance::Invariant => ConstraintDirection::Bidirectional,
ty::Variance::Bivariant => {
// We don't add edges for bivariant cases.
return;
}
};

let region = self.universal_regions.to_region_vid(region);
self.directions
.entry(region)
.and_modify(|entry| {
// If there's already a recorded direction for this region, we combine the two:
// - combining the same direction is idempotent
// - combining different directions is trivially bidirectional
if entry != &direction {
*entry = ConstraintDirection::Bidirectional;
}
})
.or_insert(direction);
}
}

impl<'tcx> TypeRelation<TyCtxt<'tcx>> for VarianceExtractor<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.tcx
}

fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
&mut self,
variance: ty::Variance,
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
a: T,
b: T,
) -> RelateResult<'tcx, T> {
let old_ambient_variance = self.ambient_variance;
self.ambient_variance = self.ambient_variance.xform(variance);
let r = self.relate(a, b)?;
self.ambient_variance = old_ambient_variance;
Ok(r)
}

fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
relate::structurally_relate_tys(self, a, b)
}

fn regions(
&mut self,
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
self.record_variance(a, self.ambient_variance);
Ok(a)
}

fn consts(
&mut self,
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
relate::structurally_relate_consts(self, a, b)
}

fn binders<T>(
&mut self,
a: ty::Binder<'tcx, T>,
_: ty::Binder<'tcx, T>,
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
where
T: Relate<TyCtxt<'tcx>>,
{
self.relate(a.skip_binder(), a.skip_binder())?;
Ok(a)
}
}
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/polonius/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
mod constraints;
mod dump;
pub(crate) mod legacy;
mod liveness_constraints;

use std::collections::BTreeMap;

Expand Down

0 comments on commit de049e6

Please sign in to comment.