Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 6f4ea40

Browse files
committed
Rewrite handling of placeholder regions into outlives constraints
This commit prepares for Polonius by moving handling of leak check/universe errors out of the inference step by rewriting any universe error into an outlives-static constraint. This variant is a work in progress. Note that a few debug assertions no longer hold; a few extra eyes on those changes are appreciated!
1 parent b14d8b2 commit 6f4ea40

File tree

2 files changed

+94
-9
lines changed
  • compiler/rustc_borrowck/src

2 files changed

+94
-9
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
use rustc_data_structures::fx::FxHashSet;
12
use rustc_data_structures::graph::scc::Sccs;
23
use rustc_index::{IndexSlice, IndexVec};
4+
use rustc_infer::infer::NllRegionVariableOrigin;
35
use rustc_middle::mir::ConstraintCategory;
46
use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
57
use rustc_span::Span;
68
use std::fmt;
79
use std::ops::Index;
810

11+
use crate::region_infer::RegionDefinition;
912
use crate::type_check::Locations;
13+
use crate::universal_regions::UniversalRegions;
1014

1115
pub(crate) mod graph;
1216

@@ -62,6 +66,68 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
6266
) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
6367
&self.outlives
6468
}
69+
pub(crate) fn placeholders_to_static(
70+
&self,
71+
universal_regions: &UniversalRegions<'tcx>,
72+
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
73+
) -> Self {
74+
let mut copy = self.clone();
75+
76+
let universe = |rvid: RegionVid| definitions[rvid].universe;
77+
78+
let is_placeholder = |rvid: RegionVid| {
79+
matches!(definitions[rvid].origin, NllRegionVariableOrigin::Placeholder(_))
80+
};
81+
82+
let outlives_static = |rvid: RegionVid| OutlivesConstraint {
83+
sup: rvid,
84+
sub: universal_regions.fr_static, // All the following values are made up ex nihil
85+
category: ConstraintCategory::BoringNoLocation,
86+
locations: Locations::All(rustc_span::DUMMY_SP),
87+
span: rustc_span::DUMMY_SP,
88+
variance_info: VarianceDiagInfo::None,
89+
from_closure: false,
90+
};
91+
92+
let outlived_regions = |longer: RegionVid| {
93+
self.outlives().iter().filter_map(move |OutlivesConstraint { sub, sup, .. }| {
94+
(*sup == longer).then_some(sub)
95+
})
96+
};
97+
98+
let should_be_static = definitions.indices().filter_map(|rvid| {
99+
let mut queue: Vec<RegionVid> = outlived_regions(rvid).map(|&r| r).collect();
100+
let mut seen: FxHashSet<_> = queue.iter().map(|&r| r).collect();
101+
seen.insert(rvid);
102+
debug!(?rvid);
103+
debug!("rvid definition: {:?}", definitions[rvid]);
104+
while let Some(outlived) = queue.pop() {
105+
if is_placeholder(outlived) && universe(outlived) > universe(rvid) {
106+
debug!(
107+
"{rvid:?}: {outlived:?} => {rvid:?}: 'static, universe leak ({:?} > {:?})",
108+
universe(outlived),
109+
universe(rvid)
110+
);
111+
return Some(rvid);
112+
} else {
113+
for &r in outlived_regions(outlived) {
114+
if seen.contains(&r) {
115+
continue;
116+
}
117+
seen.insert(r);
118+
queue.push(r);
119+
}
120+
}
121+
}
122+
None
123+
});
124+
125+
for rvid in should_be_static {
126+
copy.push(outlives_static(rvid));
127+
}
128+
129+
copy
130+
}
65131
}
66132

67133
impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ pub struct RegionInferenceContext<'tcx> {
121121
/// Information about how the universally quantified regions in
122122
/// scope on this function relate to one another.
123123
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
124+
125+
/// Whether we are operating with polonius=next or not. False
126+
/// means the regular NLL machinery is in use, true means use the
127+
/// new Polonius constraint propagation.
128+
polonius_next_enabled: bool,
124129
}
125130

126131
/// Each time that `apply_member_constraint` is successful, it appends
@@ -322,7 +327,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
322327
/// The `outlives_constraints` and `type_tests` are an initial set
323328
/// of constraints produced by the MIR type check.
324329
pub(crate) fn new<'cx>(
325-
_infcx: &BorrowckInferCtxt<'cx, 'tcx>,
330+
infcx: &BorrowckInferCtxt<'cx, 'tcx>,
326331
var_infos: VarInfos,
327332
universal_regions: Rc<UniversalRegions<'tcx>>,
328333
placeholder_indices: Rc<PlaceholderIndices>,
@@ -345,13 +350,21 @@ impl<'tcx> RegionInferenceContext<'tcx> {
345350
.map(|info| RegionDefinition::new(info.universe, info.origin))
346351
.collect();
347352

348-
let constraints = Frozen::freeze(outlives_constraints);
353+
let polonius_next_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled();
354+
355+
let constraints = if polonius_next_enabled {
356+
Frozen::freeze(
357+
outlives_constraints.placeholders_to_static(&universal_regions, &definitions),
358+
)
359+
} else {
360+
Frozen::freeze(outlives_constraints)
361+
};
349362
let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
350363
let fr_static = universal_regions.fr_static;
351364
let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static));
352365

353366
if cfg!(debug_assertions) {
354-
sccs_info(_infcx, constraint_sccs.clone());
367+
sccs_info(infcx, constraint_sccs.clone());
355368
}
356369

357370
let mut scc_values =
@@ -386,6 +399,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
386399
type_tests,
387400
universal_regions,
388401
universal_region_relations,
402+
polonius_next_enabled,
389403
};
390404

391405
result.init_free_and_bound_regions();
@@ -535,7 +549,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
535549
// This iterator has unstable order but we collect it all into an IndexVec
536550
for (external_name, variable) in self.universal_regions.named_universal_regions() {
537551
debug!(
538-
"init_universal_regions: region {:?} has external name {:?}",
552+
"init_free_and_bound_regions: region {:?} has external name {:?}",
539553
variable, external_name
540554
);
541555
self.definitions[variable].external_name = Some(external_name);
@@ -561,8 +575,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
561575
// its universe `ui` and its extensions. So we
562576
// can't just add it into `scc` unless the
563577
// universe of the scc can name this region.
578+
// This case is handled separately when Polonius
579+
// is enabled.
564580
let scc_universe = self.scc_universes[scc];
565-
if scc_universe.can_name(placeholder.universe) {
581+
if self.polonius_next_enabled || scc_universe.can_name(placeholder.universe) {
566582
self.scc_values.add_element(scc, placeholder);
567583
} else {
568584
debug!(
@@ -760,12 +776,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
760776
// Walk each SCC `B` such that `A: B`...
761777
for &scc_b in constraint_sccs.successors(scc_a) {
762778
debug!(?scc_b);
763-
764779
// ...and add elements from `B` into `A`. One complication
765780
// arises because of universes: If `B` contains something
766781
// that `A` cannot name, then `A` can only contain `B` if
767782
// it outlives static.
768-
if self.universe_compatible(scc_b, scc_a) {
783+
if self.polonius_next_enabled || self.universe_compatible(scc_b, scc_a) {
769784
// `A` can name everything that is in `B`, so just
770785
// merge the bits.
771786
self.scc_values.add_region(scc_a, scc_b);
@@ -828,7 +843,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
828843
return;
829844
}
830845
debug_assert!(
831-
self.scc_values.placeholders_contained_in(scc).next().is_none(),
846+
self.polonius_next_enabled
847+
|| self.scc_values.placeholders_contained_in(scc).next().is_none(),
832848
"scc {:?} in a member constraint has placeholder value: {:?}",
833849
scc,
834850
self.scc_values.region_value_str(scc),
@@ -1563,7 +1579,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
15631579
// Because this free region must be in the ROOT universe, we
15641580
// know it cannot contain any bound universes.
15651581
assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT);
1566-
debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none());
1582+
debug_assert!(
1583+
self.polonius_next_enabled
1584+
|| self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none()
1585+
);
15671586

15681587
// Only check all of the relations for the main representative of each
15691588
// SCC, otherwise just check that we outlive said representative. This

0 commit comments

Comments
 (0)