Use a visitor that could be reused by opaque type capture check later
This commit is contained in:
parent
371d8a8215
commit
1005970485
@ -3,17 +3,13 @@
|
||||
use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
|
||||
use rustc_index::interval::IntervalSet;
|
||||
use rustc_infer::infer::canonical::QueryRegionConstraints;
|
||||
use rustc_infer::infer::outlives::test_type_match;
|
||||
use rustc_infer::infer::region_constraints::VerifyIfEq;
|
||||
use rustc_infer::infer::outlives::for_liveness;
|
||||
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
|
||||
use rustc_middle::traits::query::DropckOutlivesResult;
|
||||
use rustc_middle::ty::{
|
||||
self, RegionVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
|
||||
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
|
||||
use std::ops::ControlFlow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
@ -612,107 +608,25 @@ fn make_all_regions_live(
|
||||
let num_loans = typeck.borrowck_context.borrow_set.len();
|
||||
let value_loans = &mut HybridBitSet::new_empty(num_loans);
|
||||
|
||||
struct MakeAllRegionsLive<'a, 'b, 'tcx> {
|
||||
typeck: &'b mut TypeChecker<'a, 'tcx>,
|
||||
live_at: &'b IntervalSet<PointIndex>,
|
||||
value_loans: &'b mut HybridBitSet<BorrowIndex>,
|
||||
inflowing_loans: &'b SparseBitMatrix<RegionVid, BorrowIndex>,
|
||||
}
|
||||
impl<'tcx> MakeAllRegionsLive<'_, '_, 'tcx> {
|
||||
/// We can prove that an alias is live two ways:
|
||||
/// 1. All the components are live.
|
||||
/// 2. There is a known outlives bound or where-clause, and that
|
||||
/// region is live.
|
||||
/// We search through the item bounds and where clauses for
|
||||
/// either `'static` or a unique outlives region, and if one is
|
||||
/// found, we just need to prove that that region is still live.
|
||||
/// If one is not found, then we continue to walk through the alias.
|
||||
fn make_alias_live(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
|
||||
let ty::Alias(_kind, alias_ty) = t.kind() else {
|
||||
bug!("`make_alias_live` only takes alias types");
|
||||
};
|
||||
let tcx = self.typeck.infcx.tcx;
|
||||
let param_env = self.typeck.param_env;
|
||||
let outlives_bounds: Vec<_> = tcx
|
||||
.item_bounds(alias_ty.def_id)
|
||||
.iter_instantiated(tcx, alias_ty.args)
|
||||
.filter_map(|clause| {
|
||||
if let Some(outlives) = clause.as_type_outlives_clause()
|
||||
&& outlives.skip_binder().0 == t
|
||||
{
|
||||
Some(outlives.skip_binder().1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.chain(param_env.caller_bounds().iter().filter_map(|clause| {
|
||||
let outlives = clause.as_type_outlives_clause()?;
|
||||
if let Some(outlives) = outlives.no_bound_vars()
|
||||
&& outlives.0 == t
|
||||
{
|
||||
Some(outlives.1)
|
||||
} else {
|
||||
test_type_match::extract_verify_if_eq(
|
||||
tcx,
|
||||
param_env,
|
||||
&outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| {
|
||||
VerifyIfEq { ty, bound }
|
||||
}),
|
||||
t,
|
||||
)
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
// If we find `'static`, then we know the alias doesn't capture *any* regions.
|
||||
// Otherwise, all of the outlives regions should be equal -- if they're not,
|
||||
// we don't really know how to proceed, so we continue recursing through the
|
||||
// alias.
|
||||
if outlives_bounds.contains(&tcx.lifetimes.re_static) {
|
||||
ControlFlow::Continue(())
|
||||
} else if let Some(r) = outlives_bounds.first()
|
||||
&& outlives_bounds[1..].iter().all(|other_r| other_r == r)
|
||||
{
|
||||
r.visit_with(self)
|
||||
} else {
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MakeAllRegionsLive<'_, '_, 'tcx> {
|
||||
type BreakTy = !;
|
||||
value.visit_with(&mut for_liveness::FreeRegionsVisitor {
|
||||
tcx: typeck.tcx(),
|
||||
param_env: typeck.param_env,
|
||||
op: |r| {
|
||||
let live_region_vid = typeck.borrowck_context.universal_regions.to_region_vid(r);
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if r.is_late_bound() {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
let live_region_vid =
|
||||
self.typeck.borrowck_context.universal_regions.to_region_vid(r);
|
||||
|
||||
self.typeck
|
||||
typeck
|
||||
.borrowck_context
|
||||
.constraints
|
||||
.liveness_constraints
|
||||
.add_elements(live_region_vid, self.live_at);
|
||||
.add_elements(live_region_vid, live_at);
|
||||
|
||||
// There can only be inflowing loans for this region when we are using
|
||||
// `-Zpolonius=next`.
|
||||
if let Some(inflowing) = self.inflowing_loans.row(live_region_vid) {
|
||||
self.value_loans.union(inflowing);
|
||||
if let Some(inflowing) = inflowing_loans.row(live_region_vid) {
|
||||
value_loans.union(inflowing);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if !t.has_free_regions() {
|
||||
ControlFlow::Continue(())
|
||||
} else if let ty::Alias(..) = t.kind() {
|
||||
self.make_alias_live(t)
|
||||
} else {
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
value.visit_with(&mut MakeAllRegionsLive { typeck, live_at, value_loans, inflowing_loans });
|
||||
},
|
||||
});
|
||||
|
||||
// Record the loans reaching the value.
|
||||
if !value_loans.is_empty() {
|
||||
|
@ -380,6 +380,8 @@ pub fn register_member_constraints(
|
||||
.collect(),
|
||||
);
|
||||
|
||||
// FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
|
||||
// not currently sound until we have existential regions.
|
||||
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
|
||||
tcx: self.tcx,
|
||||
op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions),
|
||||
|
121
compiler/rustc_infer/src/infer/outlives/for_liveness.rs
Normal file
121
compiler/rustc_infer/src/infer/outlives/for_liveness.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::infer::outlives::test_type_match;
|
||||
use crate::infer::region_constraints::VerifyIfEq;
|
||||
|
||||
/// Visits free regions in the type that are relevant for liveness computation.
|
||||
/// These regions are passed to `OP`.
|
||||
///
|
||||
/// Specifically, we visit all of the regions of types recursively, except if
|
||||
/// the type is an alias, we look at the outlives bounds in the param-env
|
||||
/// and alias's item bounds. If there is a unique outlives bound, then visit
|
||||
/// that instead. If there is not a unique but there is a `'static` outlives
|
||||
/// bound, then don't visit anything. Otherwise, walk through the opaque's
|
||||
/// regions structurally.
|
||||
pub struct FreeRegionsVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
pub op: OP,
|
||||
}
|
||||
|
||||
impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for FreeRegionsVisitor<'tcx, OP>
|
||||
where
|
||||
OP: FnMut(ty::Region<'tcx>),
|
||||
{
|
||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
t: &ty::Binder<'tcx, T>,
|
||||
) -> ControlFlow<Self::BreakTy> {
|
||||
t.super_visit_with(self);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match *r {
|
||||
// ignore bound regions, keep visiting
|
||||
ty::ReLateBound(_, _) => ControlFlow::Continue(()),
|
||||
_ => {
|
||||
(self.op)(r);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
// We're only interested in types involving regions
|
||||
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
match ty.kind() {
|
||||
// We can prove that an alias is live two ways:
|
||||
// 1. All the components are live.
|
||||
//
|
||||
// 2. There is a known outlives bound or where-clause, and that
|
||||
// region is live.
|
||||
//
|
||||
// We search through the item bounds and where clauses for
|
||||
// either `'static` or a unique outlives region, and if one is
|
||||
// found, we just need to prove that that region is still live.
|
||||
// If one is not found, then we continue to walk through the alias.
|
||||
ty::Alias(kind, ty::AliasTy { def_id, args, .. }) => {
|
||||
let tcx = self.tcx;
|
||||
let param_env = self.param_env;
|
||||
let outlives_bounds: Vec<_> = tcx
|
||||
.item_bounds(def_id)
|
||||
.iter_instantiated(tcx, args)
|
||||
.chain(param_env.caller_bounds())
|
||||
.filter_map(|clause| {
|
||||
let outlives = clause.as_type_outlives_clause()?;
|
||||
if let Some(outlives) = outlives.no_bound_vars()
|
||||
&& outlives.0 == ty
|
||||
{
|
||||
Some(outlives.1)
|
||||
} else {
|
||||
test_type_match::extract_verify_if_eq(
|
||||
tcx,
|
||||
param_env,
|
||||
&outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| {
|
||||
VerifyIfEq { ty, bound }
|
||||
}),
|
||||
ty,
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// If we find `'static`, then we know the alias doesn't capture *any* regions.
|
||||
// Otherwise, all of the outlives regions should be equal -- if they're not,
|
||||
// we don't really know how to proceed, so we continue recursing through the
|
||||
// alias.
|
||||
if outlives_bounds.contains(&tcx.lifetimes.re_static) {
|
||||
// no
|
||||
} else if let Some(r) = outlives_bounds.first()
|
||||
&& outlives_bounds[1..].iter().all(|other_r| other_r == r)
|
||||
{
|
||||
assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
|
||||
r.visit_with(self)?;
|
||||
} else {
|
||||
// Skip lifetime parameters that are not captures.
|
||||
let variances = match kind {
|
||||
ty::Opaque => Some(self.tcx.variances_of(*def_id)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
for (idx, s) in args.iter().enumerate() {
|
||||
if variances.map(|variances| variances[idx]) != Some(ty::Variance::Bivariant) {
|
||||
s.visit_with(self)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
ty.super_visit_with(self)?;
|
||||
}
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
|
||||
pub mod components;
|
||||
pub mod env;
|
||||
pub mod for_liveness;
|
||||
pub mod obligations;
|
||||
pub mod test_type_match;
|
||||
pub mod verify;
|
||||
|
Loading…
Reference in New Issue
Block a user