Skip to content

Commit bba679d

Browse files
Use a visitor that could be reused by opaque type capture check later
1 parent 371d8a8 commit bba679d

File tree

4 files changed

+179
-99
lines changed

4 files changed

+179
-99
lines changed

compiler/rustc_borrowck/src/type_check/liveness/trace.rs

+13-99
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@ use rustc_data_structures::graph::WithSuccessors;
33
use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
44
use rustc_index::interval::IntervalSet;
55
use rustc_infer::infer::canonical::QueryRegionConstraints;
6-
use rustc_infer::infer::outlives::test_type_match;
7-
use rustc_infer::infer::region_constraints::VerifyIfEq;
6+
use rustc_infer::infer::outlives::for_liveness::FreeRegionsVisitor;
87
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
98
use rustc_middle::traits::query::DropckOutlivesResult;
10-
use rustc_middle::ty::{
11-
self, RegionVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
12-
};
9+
use rustc_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
1310
use rustc_span::DUMMY_SP;
1411
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
1512
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
16-
use std::ops::ControlFlow;
1713
use std::rc::Rc;
1814

1915
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
@@ -612,107 +608,25 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
612608
let num_loans = typeck.borrowck_context.borrow_set.len();
613609
let value_loans = &mut HybridBitSet::new_empty(num_loans);
614610

615-
struct MakeAllRegionsLive<'a, 'b, 'tcx> {
616-
typeck: &'b mut TypeChecker<'a, 'tcx>,
617-
live_at: &'b IntervalSet<PointIndex>,
618-
value_loans: &'b mut HybridBitSet<BorrowIndex>,
619-
inflowing_loans: &'b SparseBitMatrix<RegionVid, BorrowIndex>,
620-
}
621-
impl<'tcx> MakeAllRegionsLive<'_, '_, 'tcx> {
622-
/// We can prove that an alias is live two ways:
623-
/// 1. All the components are live.
624-
/// 2. There is a known outlives bound or where-clause, and that
625-
/// region is live.
626-
/// We search through the item bounds and where clauses for
627-
/// either `'static` or a unique outlives region, and if one is
628-
/// found, we just need to prove that that region is still live.
629-
/// If one is not found, then we continue to walk through the alias.
630-
fn make_alias_live(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
631-
let ty::Alias(_kind, alias_ty) = t.kind() else {
632-
bug!("`make_alias_live` only takes alias types");
633-
};
634-
let tcx = self.typeck.infcx.tcx;
635-
let param_env = self.typeck.param_env;
636-
let outlives_bounds: Vec<_> = tcx
637-
.item_bounds(alias_ty.def_id)
638-
.iter_instantiated(tcx, alias_ty.args)
639-
.filter_map(|clause| {
640-
if let Some(outlives) = clause.as_type_outlives_clause()
641-
&& outlives.skip_binder().0 == t
642-
{
643-
Some(outlives.skip_binder().1)
644-
} else {
645-
None
646-
}
647-
})
648-
.chain(param_env.caller_bounds().iter().filter_map(|clause| {
649-
let outlives = clause.as_type_outlives_clause()?;
650-
if let Some(outlives) = outlives.no_bound_vars()
651-
&& outlives.0 == t
652-
{
653-
Some(outlives.1)
654-
} else {
655-
test_type_match::extract_verify_if_eq(
656-
tcx,
657-
param_env,
658-
&outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| {
659-
VerifyIfEq { ty, bound }
660-
}),
661-
t,
662-
)
663-
}
664-
}))
665-
.collect();
666-
// If we find `'static`, then we know the alias doesn't capture *any* regions.
667-
// Otherwise, all of the outlives regions should be equal -- if they're not,
668-
// we don't really know how to proceed, so we continue recursing through the
669-
// alias.
670-
if outlives_bounds.contains(&tcx.lifetimes.re_static) {
671-
ControlFlow::Continue(())
672-
} else if let Some(r) = outlives_bounds.first()
673-
&& outlives_bounds[1..].iter().all(|other_r| other_r == r)
674-
{
675-
r.visit_with(self)
676-
} else {
677-
t.super_visit_with(self)
678-
}
679-
}
680-
}
681-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MakeAllRegionsLive<'_, '_, 'tcx> {
682-
type BreakTy = !;
683-
684-
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
685-
if r.is_late_bound() {
686-
return ControlFlow::Continue(());
687-
}
688-
let live_region_vid =
689-
self.typeck.borrowck_context.universal_regions.to_region_vid(r);
611+
value.visit_with(&mut FreeRegionsVisitor {
612+
tcx: typeck.tcx(),
613+
param_env: typeck.param_env,
614+
op: |r| {
615+
let live_region_vid = typeck.borrowck_context.universal_regions.to_region_vid(r);
690616

691-
self.typeck
617+
typeck
692618
.borrowck_context
693619
.constraints
694620
.liveness_constraints
695-
.add_elements(live_region_vid, self.live_at);
621+
.add_elements(live_region_vid, live_at);
696622

697623
// There can only be inflowing loans for this region when we are using
698624
// `-Zpolonius=next`.
699-
if let Some(inflowing) = self.inflowing_loans.row(live_region_vid) {
700-
self.value_loans.union(inflowing);
625+
if let Some(inflowing) = inflowing_loans.row(live_region_vid) {
626+
value_loans.union(inflowing);
701627
}
702-
ControlFlow::Continue(())
703-
}
704-
705-
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
706-
if !t.has_free_regions() {
707-
ControlFlow::Continue(())
708-
} else if let ty::Alias(..) = t.kind() {
709-
self.make_alias_live(t)
710-
} else {
711-
t.super_visit_with(self)
712-
}
713-
}
714-
}
715-
value.visit_with(&mut MakeAllRegionsLive { typeck, live_at, value_loans, inflowing_loans });
628+
},
629+
});
716630

717631
// Record the loans reaching the value.
718632
if !value_loans.is_empty() {

compiler/rustc_infer/src/infer/opaque_types.rs

+2
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ impl<'tcx> InferCtxt<'tcx> {
380380
.collect(),
381381
);
382382

383+
// FIXME(#42940): This should use the `FreeRegionsVisitor`, but that's
384+
// not currently sound until we have existential regions.
383385
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
384386
tcx: self.tcx,
385387
op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
2+
3+
use std::ops::ControlFlow;
4+
5+
use crate::infer::outlives::test_type_match;
6+
use crate::infer::region_constraints::VerifyIfEq;
7+
8+
/// Visits free regions in the type that are relevant for liveness computation.
9+
/// These regions are passed to `OP`.
10+
///
11+
/// Specifically, we visit all of the regions of types recursively, except:
12+
///
13+
/// 1. If the type is an alias, we look at the outlives bounds in the param-env
14+
/// and alias's item bounds. If there is a unique outlives bound, then visit
15+
/// that instead. If there is not a unique but there is a `'static` outlives
16+
/// bound, then don't visit anything. Otherwise, walk through the opaque's
17+
/// regions structurally.
18+
///
19+
/// 2. If the type is a closure, only walk through the signature of the closure
20+
/// and the upvars. Similarly, if the type is a generator, walk through the
21+
/// three "signature" types (yield/resume/return) and its upvars. All generator
22+
/// interior regions are bound regions, so ther is no need to walk through
23+
/// that type.
24+
///
25+
/// # How does this differ from other region visitors?
26+
///
27+
/// We cannot use `push_outlives_components` because regions in closure
28+
/// signatures are not included in their outlives components. We need to
29+
/// ensure all regions outlive the given bound so that we don't end up with,
30+
/// say, `ReVar` appearing in a return type and causing ICEs when other
31+
/// functions end up with region constraints involving regions from other
32+
/// functions.
33+
///
34+
/// We also cannot use `for_each_free_region` because for closures it includes
35+
/// the regions parameters from the enclosing item, which we know are always
36+
/// universal, so we don't particularly care about since they're not relevant
37+
/// for opaque type captures or computing liveness.
38+
pub struct FreeRegionsVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
39+
pub tcx: TyCtxt<'tcx>,
40+
pub param_env: ty::ParamEnv<'tcx>,
41+
pub op: OP,
42+
}
43+
44+
impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for FreeRegionsVisitor<'tcx, OP>
45+
where
46+
OP: FnMut(ty::Region<'tcx>),
47+
{
48+
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
49+
&mut self,
50+
t: &ty::Binder<'tcx, T>,
51+
) -> ControlFlow<Self::BreakTy> {
52+
t.super_visit_with(self);
53+
ControlFlow::Continue(())
54+
}
55+
56+
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
57+
match *r {
58+
// ignore bound regions, keep visiting
59+
ty::ReLateBound(_, _) => ControlFlow::Continue(()),
60+
_ => {
61+
(self.op)(r);
62+
ControlFlow::Continue(())
63+
}
64+
}
65+
}
66+
67+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
68+
// We're only interested in types involving regions
69+
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
70+
return ControlFlow::Continue(());
71+
}
72+
73+
match ty.kind() {
74+
ty::Closure(_, ref args) => {
75+
// Skip lifetime parameters of the enclosing item(s)
76+
77+
for upvar in args.as_closure().upvar_tys() {
78+
upvar.visit_with(self);
79+
}
80+
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
81+
}
82+
83+
ty::Generator(_, ref args, _) => {
84+
// Skip lifetime parameters of the enclosing item(s)
85+
// Also skip the witness type, because that has no free regions.
86+
87+
for upvar in args.as_generator().upvar_tys() {
88+
upvar.visit_with(self);
89+
}
90+
args.as_generator().return_ty().visit_with(self);
91+
args.as_generator().yield_ty().visit_with(self);
92+
args.as_generator().resume_ty().visit_with(self);
93+
}
94+
95+
// We can prove that an alias is live two ways:
96+
// 1. All the components are live.
97+
//
98+
// 2. There is a known outlives bound or where-clause, and that
99+
// region is live.
100+
//
101+
// We search through the item bounds and where clauses for
102+
// either `'static` or a unique outlives region, and if one is
103+
// found, we just need to prove that that region is still live.
104+
// If one is not found, then we continue to walk through the alias.
105+
ty::Alias(kind, ty::AliasTy { def_id, args, .. }) => {
106+
let tcx = self.tcx;
107+
let param_env = self.param_env;
108+
let outlives_bounds: Vec<_> = tcx
109+
.item_bounds(def_id)
110+
.iter_instantiated(tcx, args)
111+
.chain(param_env.caller_bounds())
112+
.filter_map(|clause| {
113+
let outlives = clause.as_type_outlives_clause()?;
114+
if let Some(outlives) = outlives.no_bound_vars()
115+
&& outlives.0 == ty
116+
{
117+
Some(outlives.1)
118+
} else {
119+
test_type_match::extract_verify_if_eq(
120+
tcx,
121+
param_env,
122+
&outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| {
123+
VerifyIfEq { ty, bound }
124+
}),
125+
ty,
126+
)
127+
}
128+
})
129+
.collect();
130+
// If we find `'static`, then we know the alias doesn't capture *any* regions.
131+
// Otherwise, all of the outlives regions should be equal -- if they're not,
132+
// we don't really know how to proceed, so we continue recursing through the
133+
// alias.
134+
if outlives_bounds.contains(&tcx.lifetimes.re_static) {
135+
// no
136+
} else if let Some(r) = outlives_bounds.first()
137+
&& outlives_bounds[1..].iter().all(|other_r| other_r == r)
138+
{
139+
assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS));
140+
r.visit_with(self)?;
141+
} else {
142+
// Skip lifetime parameters that are not captures.
143+
let variances = match kind {
144+
ty::Opaque => Some(self.tcx.variances_of(*def_id)),
145+
_ => None,
146+
};
147+
148+
for (idx, s) in args.iter().enumerate() {
149+
if variances.map(|variances| variances[idx]) != Some(ty::Variance::Bivariant) {
150+
s.visit_with(self)?;
151+
}
152+
}
153+
}
154+
}
155+
156+
_ => {
157+
ty.super_visit_with(self)?;
158+
}
159+
}
160+
161+
ControlFlow::Continue(())
162+
}
163+
}

compiler/rustc_infer/src/infer/outlives/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_middle::ty;
99

1010
pub mod components;
1111
pub mod env;
12+
pub mod for_liveness;
1213
pub mod obligations;
1314
pub mod test_type_match;
1415
pub mod verify;

0 commit comments

Comments
 (0)