Skip to content

Commit 1b9e1e6

Browse files
committed
Auto merge of rust-lang#123720 - amandasystems:dyn-enable-refactor, r=<try>
Rewrite handling of universe-leaking 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 but seems to pass most tests. Note that a few debug assertions no longer hold; a few extra eyes on those changes are appreciated!
2 parents bd71213 + 5a1c148 commit 1b9e1e6

File tree

4 files changed

+88
-71
lines changed

4 files changed

+88
-71
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

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

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

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
6666
ConstraintCategory::Predicate(_)
6767
| ConstraintCategory::Boring
6868
| ConstraintCategory::BoringNoLocation
69-
| ConstraintCategory::Internal => "",
69+
| ConstraintCategory::Internal
70+
| ConstraintCategory::IllegalUniverse => "",
7071
}
7172
}
7273
}

compiler/rustc_borrowck/src/region_infer/mod.rs

+21-70
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub struct RegionInferenceContext<'tcx> {
7171
/// The SCC computed from `constraints` and the constraint
7272
/// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
7373
/// compute the values of each region.
74-
constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
74+
constraint_sccs: Sccs<RegionVid, ConstraintSccIndex>,
7575

7676
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` exists if
7777
/// `B: A`. This is used to compute the universal regions that are required
@@ -251,7 +251,7 @@ pub enum ExtraConstraintInfo {
251251
#[instrument(skip(infcx, sccs), level = "debug")]
252252
fn sccs_info<'cx, 'tcx>(
253253
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
254-
sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
254+
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
255255
) {
256256
use crate::renumber::RegionCtxt;
257257

@@ -322,12 +322,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
322322
/// The `outlives_constraints` and `type_tests` are an initial set
323323
/// of constraints produced by the MIR type check.
324324
pub(crate) fn new<'cx>(
325-
_infcx: &BorrowckInferCtxt<'cx, 'tcx>,
325+
infcx: &BorrowckInferCtxt<'cx, 'tcx>,
326326
var_infos: VarInfos,
327327
universal_regions: Rc<UniversalRegions<'tcx>>,
328328
placeholder_indices: Rc<PlaceholderIndices>,
329329
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
330-
outlives_constraints: OutlivesConstraintSet<'tcx>,
330+
mut outlives_constraints: OutlivesConstraintSet<'tcx>,
331331
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
332332
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
333333
type_tests: Vec<TypeTest<'tcx>>,
@@ -345,13 +345,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
345345
.map(|info| RegionDefinition::new(info.universe, info.origin))
346346
.collect();
347347

348+
outlives_constraints.placeholders_to_static(&universal_regions, &definitions);
349+
348350
let constraints = Frozen::freeze(outlives_constraints);
349351
let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
350352
let fr_static = universal_regions.fr_static;
351-
let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static));
353+
let constraint_sccs = constraints.compute_sccs(&constraint_graph, fr_static);
352354

353355
if cfg!(debug_assertions) {
354-
sccs_info(_infcx, constraint_sccs.clone());
356+
sccs_info(infcx, &constraint_sccs);
355357
}
356358

357359
let mut scc_values =
@@ -535,7 +537,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
535537
// This iterator has unstable order but we collect it all into an IndexVec
536538
for (external_name, variable) in self.universal_regions.named_universal_regions() {
537539
debug!(
538-
"init_universal_regions: region {:?} has external name {:?}",
540+
"init_free_and_bound_regions: region {:?} has external name {:?}",
539541
variable, external_name
540542
);
541543
self.definitions[variable].external_name = Some(external_name);
@@ -557,21 +559,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
557559
}
558560

559561
NllRegionVariableOrigin::Placeholder(placeholder) => {
560-
// Each placeholder region is only visible from
561-
// its universe `ui` and its extensions. So we
562-
// can't just add it into `scc` unless the
563-
// universe of the scc can name this region.
564-
let scc_universe = self.scc_universes[scc];
565-
if scc_universe.can_name(placeholder.universe) {
566-
self.scc_values.add_element(scc, placeholder);
567-
} else {
568-
debug!(
569-
"init_free_and_bound_regions: placeholder {:?} is \
570-
not compatible with universe {:?} of its SCC {:?}",
571-
placeholder, scc_universe, scc,
572-
);
573-
self.add_incompatible_universe(scc);
574-
}
562+
self.scc_values.add_element(scc, placeholder);
575563
}
576564

577565
NllRegionVariableOrigin::Existential { .. } => {
@@ -739,8 +727,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
739727
// SCC. For each SCC, we visit its successors and compute
740728
// their values, then we union all those values to get our
741729
// own.
742-
let constraint_sccs = self.constraint_sccs.clone();
743-
for scc in constraint_sccs.all_sccs() {
730+
for scc in self.constraint_sccs.all_sccs() {
744731
self.compute_value_for_scc(scc);
745732
}
746733

@@ -755,23 +742,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
755742
/// (which is assured by iterating over SCCs in dependency order).
756743
#[instrument(skip(self), level = "debug")]
757744
fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
758-
let constraint_sccs = self.constraint_sccs.clone();
759-
760745
// Walk each SCC `B` such that `A: B`...
761-
for &scc_b in constraint_sccs.successors(scc_a) {
746+
for &scc_b in self.constraint_sccs.successors(scc_a) {
762747
debug!(?scc_b);
763-
764-
// ...and add elements from `B` into `A`. One complication
765-
// arises because of universes: If `B` contains something
766-
// that `A` cannot name, then `A` can only contain `B` if
767-
// it outlives static.
768-
if self.universe_compatible(scc_b, scc_a) {
769-
// `A` can name everything that is in `B`, so just
770-
// merge the bits.
771-
self.scc_values.add_region(scc_a, scc_b);
772-
} else {
773-
self.add_incompatible_universe(scc_a);
774-
}
748+
self.scc_values.add_region(scc_a, scc_b);
775749
}
776750

777751
// Now take member constraints into account.
@@ -827,12 +801,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
827801
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
828802
return;
829803
}
830-
debug_assert!(
831-
self.scc_values.placeholders_contained_in(scc).next().is_none(),
832-
"scc {:?} in a member constraint has placeholder value: {:?}",
833-
scc,
834-
self.scc_values.region_value_str(scc),
835-
);
836804

837805
// The existing value for `scc` is a lower-bound. This will
838806
// consist of some set `{P} + {LB}` of points `{P}` and
@@ -917,21 +885,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
917885
self.scc_values.placeholders_contained_in(scc_b).all(|p| universe_a.can_name(p.universe))
918886
}
919887

920-
/// Extend `scc` so that it can outlive some placeholder region
921-
/// from a universe it can't name; at present, the only way for
922-
/// this to be true is if `scc` outlives `'static`. This is
923-
/// actually stricter than necessary: ideally, we'd support bounds
924-
/// like `for<'a: 'b>` that might then allow us to approximate
925-
/// `'a` with `'b` and not `'static`. But it will have to do for
926-
/// now.
927-
fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) {
928-
debug!("add_incompatible_universe(scc={:?})", scc);
929-
930-
let fr_static = self.universal_regions.fr_static;
931-
self.scc_values.add_all_points(scc);
932-
self.scc_values.add_element(scc, fr_static);
933-
}
934-
935888
/// Once regions have been propagated, this method is used to see
936889
/// whether the "type tests" produced by typeck were satisfied;
937890
/// type tests encode type-outlives relationships like `T:
@@ -1563,7 +1516,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
15631516
// Because this free region must be in the ROOT universe, we
15641517
// know it cannot contain any bound universes.
15651518
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());
15671519

15681520
// Only check all of the relations for the main representative of each
15691521
// SCC, otherwise just check that we outlive said representative. This
@@ -1772,20 +1724,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17721724
/// that cannot be named by `fr1`; in that case, we will require
17731725
/// that `fr1: 'static` because it is the only way to `fr1: r` to
17741726
/// be satisfied. (See `add_incompatible_universe`.)
1727+
#[instrument(skip(self), ret)]
17751728
pub(crate) fn provides_universal_region(
17761729
&self,
17771730
r: RegionVid,
17781731
fr1: RegionVid,
17791732
fr2: RegionVid,
17801733
) -> bool {
1781-
debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
1782-
let result = {
1783-
r == fr2 || {
1784-
fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
1785-
}
1786-
};
1787-
debug!("provides_universal_region: result = {:?}", result);
1788-
result
1734+
let fr2_is_static = fr2 == self.universal_regions.fr_static;
1735+
r == fr2 || (fr2_is_static && self.cannot_name_placeholder(fr1, r))
17891736
}
17901737

17911738
/// If `r2` represents a placeholder region, then this returns
@@ -1900,6 +1847,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19001847

19011848
// This loop can be hot.
19021849
for constraint in outgoing_edges_from_graph {
1850+
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
1851+
debug!("Ignoring illegal universe constraint: {constraint:?}");
1852+
continue;
1853+
}
19031854
handle_constraint(constraint);
19041855
}
19051856

@@ -2249,7 +2200,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
22492200

22502201
/// Access to the SCC constraint graph.
22512202
pub(crate) fn constraint_sccs(&self) -> &Sccs<RegionVid, ConstraintSccIndex> {
2252-
self.constraint_sccs.as_ref()
2203+
&self.constraint_sccs
22532204
}
22542205

22552206
/// Access to the region graph, built from the outlives constraints.

compiler/rustc_middle/src/mir/query.rs

+2
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ pub enum ConstraintCategory<'tcx> {
273273

274274
/// A constraint that doesn't correspond to anything the user sees.
275275
Internal,
276+
/// An internal constraint derived from an illegal universe relation.
277+
IllegalUniverse,
276278
}
277279

278280
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]

0 commit comments

Comments
 (0)