Skip to content

Commit f455c4f

Browse files
amandasystemslcnr
andcommitted
Use an enum for SCC representatives, plus other code review
Co-authored-by: lcnr <[email protected]>
1 parent aca36fd commit f455c4f

File tree

7 files changed

+131
-136
lines changed

7 files changed

+131
-136
lines changed

compiler/rustc_borrowck/src/eliminate_placeholders.rs renamed to compiler/rustc_borrowck/src/handle_placeholders.rs

Lines changed: 71 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
//! Logic for lowering higher-kinded outlives constraints
22
//! (with placeholders and universes) and turn them into regular
33
//! outlives constraints.
4-
//!
5-
//! This logic is provisional and should be removed once the trait
6-
//! solver can handle this kind of constraint.
4+
75
use rustc_data_structures::frozen::Frozen;
8-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
6+
use rustc_data_structures::fx::FxIndexMap;
97
use rustc_data_structures::graph::scc;
108
use rustc_data_structures::graph::scc::Sccs;
119
use rustc_index::IndexVec;
10+
use rustc_infer::infer::RegionVariableOrigin;
1211
use rustc_middle::mir::ConstraintCategory;
1312
use rustc_middle::ty::{RegionVid, UniverseIndex};
1413
use tracing::debug;
@@ -18,7 +17,7 @@ use crate::consumers::OutlivesConstraint;
1817
use crate::diagnostics::UniverseInfo;
1918
use crate::member_constraints::MemberConstraintSet;
2019
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
21-
use crate::region_infer::{ConstraintSccs, RegionDefinition, TypeTest};
20+
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
2221
use crate::ty::VarianceDiagInfo;
2322
use crate::type_check::free_region_relations::UniversalRegionRelations;
2423
use crate::type_check::{Locations, MirTypeckRegionConstraints};
@@ -32,7 +31,7 @@ pub(crate) struct LoweredConstraints<'tcx> {
3231
pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
3332
pub(crate) scc_annotations: IndexVec<ConstraintSccIndex, RegionTracker>,
3433
pub(crate) member_constraints: MemberConstraintSet<'tcx, RegionVid>,
35-
pub(crate) outlives_constraints: OutlivesConstraintSet<'tcx>,
34+
pub(crate) outlives_constraints: Frozen<OutlivesConstraintSet<'tcx>>,
3635
pub(crate) type_tests: Vec<TypeTest<'tcx>>,
3736
pub(crate) liveness_constraints: LivenessValues,
3837
pub(crate) universe_causes: FxIndexMap<UniverseIndex, UniverseInfo<'tcx>>,
@@ -73,45 +72,35 @@ pub(crate) struct RegionTracker {
7372
/// This includes placeholders within this SCC.
7473
max_placeholder_universe_reached: UniverseIndex,
7574

76-
/// The smallest universe index reachable form the nodes of this SCC.
77-
min_reachable_universe: UniverseIndex,
78-
79-
/// The representative Region Variable Id for this SCC. We prefer
80-
/// placeholders over existentially quantified variables, otherwise
81-
/// it's the one with the smallest Region Variable ID.
82-
pub(crate) representative: RegionVid,
83-
84-
/// Is the current representative a placeholder?
85-
representative_is_placeholder: bool,
75+
/// The largest universe nameable from this SCC.
76+
/// It is the smallest nameable universes of all
77+
/// existential regions reachable from it.
78+
max_nameable_universe: UniverseIndex,
8679

87-
/// Is the current representative existentially quantified?
88-
representative_is_existential: bool,
80+
/// The representative Region Variable Id for this SCC.
81+
pub(crate) representative: Representative,
8982
}
9083

9184
impl RegionTracker {
9285
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
93-
let (representative_is_placeholder, representative_is_existential) = match definition.origin
94-
{
95-
NllRegionVariableOrigin::FreeRegion => (false, false),
96-
NllRegionVariableOrigin::Placeholder(_) => (true, false),
97-
NllRegionVariableOrigin::Existential { .. } => (false, true),
98-
};
99-
10086
let placeholder_universe =
101-
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
87+
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
88+
definition.universe
89+
} else {
90+
UniverseIndex::ROOT
91+
};
10292

10393
Self {
10494
max_placeholder_universe_reached: placeholder_universe,
105-
min_reachable_universe: definition.universe,
106-
representative: rvid,
107-
representative_is_placeholder,
108-
representative_is_existential,
95+
max_nameable_universe: definition.universe,
96+
representative: Representative::new(rvid, definition),
10997
}
11098
}
11199

112-
/// The smallest-indexed universe reachable from and/or in this SCC.
113-
pub(crate) fn min_universe(self) -> UniverseIndex {
114-
self.min_reachable_universe
100+
/// The largest universe this SCC can name. It's the smallest
101+
/// largest nameable uninverse of any reachable region.
102+
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
103+
self.max_nameable_universe
115104
}
116105

117106
fn merge_min_max_seen(&mut self, other: &Self) {
@@ -120,40 +109,29 @@ impl RegionTracker {
120109
other.max_placeholder_universe_reached,
121110
);
122111

123-
self.min_reachable_universe =
124-
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
112+
self.max_nameable_universe =
113+
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
125114
}
126115

127116
/// Returns `true` if during the annotated SCC reaches a placeholder
128-
/// with a universe larger than the smallest reachable one, `false` otherwise.
117+
/// with a universe larger than the smallest nameable universe of any
118+
/// reachable existential region.
129119
pub(crate) fn has_incompatible_universes(&self) -> bool {
130-
self.min_universe().cannot_name(self.max_placeholder_universe_reached)
120+
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
131121
}
132122

133-
/// Determine if the tracked universes of the two SCCs
134-
/// are compatible.
123+
/// Determine if the tracked universes of the two SCCs are compatible.
135124
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
136-
self.min_universe().can_name(other.min_universe())
137-
|| self.min_universe().can_name(other.max_placeholder_universe_reached)
125+
self.max_nameable_universe().can_name(other.max_nameable_universe())
126+
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
138127
}
139128
}
140129

141130
impl scc::Annotation for RegionTracker {
142-
fn merge_scc(mut self, mut other: Self) -> Self {
143-
// Prefer any placeholder over any existential
144-
if other.representative_is_placeholder && self.representative_is_existential {
145-
other.merge_min_max_seen(&self);
146-
return other;
147-
}
148-
149-
if self.representative_is_placeholder && other.representative_is_existential
150-
|| (self.representative <= other.representative)
151-
{
152-
self.merge_min_max_seen(&other);
153-
return self;
154-
}
155-
other.merge_min_max_seen(&self);
156-
other
131+
fn merge_scc(mut self, other: Self) -> Self {
132+
self.representative = self.representative.merge_scc(other.representative);
133+
self.merge_min_max_seen(&other);
134+
self
157135
}
158136

159137
fn merge_reached(mut self, other: Self) -> Self {
@@ -164,7 +142,7 @@ impl scc::Annotation for RegionTracker {
164142
}
165143

166144
/// Determines if the region variable definitions contain
167-
/// placeholers, and compute them for later use.
145+
/// placeholders, and compute them for later use.
168146
fn region_definitions<'tcx>(
169147
universal_regions: &UniversalRegions<'tcx>,
170148
infcx: &BorrowckInferCtxt<'tcx>,
@@ -177,42 +155,39 @@ fn region_definitions<'tcx>(
177155
let mut has_placeholders = false;
178156

179157
for info in var_infos.iter() {
180-
let definition = RegionDefinition::new(info);
181-
has_placeholders |= matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_));
158+
let origin = match info.origin {
159+
RegionVariableOrigin::Nll(origin) => origin,
160+
_ => NllRegionVariableOrigin::Existential { from_forall: false },
161+
};
162+
163+
let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
164+
165+
has_placeholders |= matches!(origin, NllRegionVariableOrigin::Placeholder(_));
182166
definitions.push(definition);
183167
}
184168

185169
// Add external names from universal regions in fun function definitions.
170+
// FIXME: this two-step method is annoying, but I don't know how to avoid it.
186171
for (external_name, variable) in universal_regions.named_universal_regions_iter() {
187172
debug!("region {:?} has external name {:?}", variable, external_name);
188173
definitions[variable].external_name = Some(external_name);
189174
}
190175
(Frozen::freeze(definitions), has_placeholders)
191176
}
192177

193-
/// This method handles Universe errors by rewriting the constraint
178+
/// This method handles placeholders by rewriting the constraint
194179
/// graph. For each strongly connected component in the constraint
195180
/// graph such that there is a series of constraints
196181
/// A: B: C: ... : X where
197-
/// A's universe is smaller than X's and A is a placeholder,
182+
/// A contains a placeholder whose universe cannot be named by X,
198183
/// add a constraint that A: 'static. This is a safe upper bound
199184
/// in the face of borrow checker/trait solver limitations that will
200185
/// eventually go away.
201186
///
202187
/// For a more precise definition, see the documentation for
203-
/// [`RegionTracker`] and its methods!.
204-
///
205-
/// Since universes can also be involved in errors (if one placeholder
206-
/// transitively outlives another), this function also flags those.
207-
///
208-
/// Additionally, it similarly rewrites type-tests.
209-
///
210-
/// This edge case used to be handled during constraint propagation
211-
/// by iterating over the strongly connected components in the constraint
212-
/// graph while maintaining a set of bookkeeping mappings similar
213-
/// to what is stored in `RegionTracker` and manually adding 'sttaic as
214-
/// needed.
188+
/// [`RegionTracker`] and its methods!
215189
///
190+
/// This edge case used to be handled during constraint propagation.
216191
/// It was rewritten as part of the Polonius project with the goal of moving
217192
/// higher-kindedness concerns out of the path of the borrow checker,
218193
/// for two reasons:
@@ -228,7 +203,7 @@ fn region_definitions<'tcx>(
228203
/// This code is a stop-gap measure in preparation for the future trait solver.
229204
///
230205
/// Every constraint added by this method is an internal `IllegalUniverse` constraint.
231-
pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
206+
pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
232207
constraints: MirTypeckRegionConstraints<'tcx>,
233208
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
234209
infcx: &BorrowckInferCtxt<'tcx>,
@@ -267,36 +242,37 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
267242
)
268243
};
269244

245+
let mut scc_annotations = SccAnnotations::init(&definitions);
246+
let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
247+
270248
// This code structure is a bit convoluted because it allows for a planned
271249
// future change where the early return here has a different type of annotation
272250
// that does much less work.
273251
if !has_placeholders {
274252
debug!("No placeholder regions found; skipping rewriting logic!");
275-
let mut scc_annotations = SccAnnotations::init(&definitions);
276-
let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations);
277253

278254
return LoweredConstraints {
279255
type_tests,
280256
member_constraints,
281257
constraint_sccs,
282258
scc_annotations: scc_annotations.scc_to_annotation,
283259
definitions,
284-
outlives_constraints,
260+
outlives_constraints: Frozen::freeze(outlives_constraints),
285261
liveness_constraints,
286262
universe_causes,
287263
placeholder_indices,
288264
};
289265
}
290266
debug!("Placeholders present; activating placeholder handling logic!");
291267

292-
let mut annotations = SccAnnotations::init(&definitions);
293-
let sccs = compute_sccs(&outlives_constraints, &mut annotations);
294-
295-
let outlives_static =
296-
rewrite_outlives(&sccs, &annotations, fr_static, &mut outlives_constraints);
268+
let added_constraints = rewrite_placeholder_outlives(
269+
&constraint_sccs,
270+
&scc_annotations,
271+
fr_static,
272+
&mut outlives_constraints,
273+
);
297274

298-
let (sccs, scc_annotations) = if !outlives_static.is_empty() {
299-
debug!("The following SCCs had :'static constraints added: {:?}", outlives_static);
275+
let (constraint_sccs, scc_annotations) = if added_constraints {
300276
let mut annotations = SccAnnotations::init(&definitions);
301277

302278
// We changed the constraint set and so must recompute SCCs.
@@ -307,31 +283,31 @@ pub(crate) fn rewrite_higher_kinded_outlives_as_constraints<'tcx>(
307283
} else {
308284
// If we didn't add any back-edges; no more work needs doing
309285
debug!("No constraints rewritten!");
310-
(sccs, annotations.scc_to_annotation)
286+
(constraint_sccs, scc_annotations.scc_to_annotation)
311287
};
312288

313289
LoweredConstraints {
314-
constraint_sccs: sccs,
290+
constraint_sccs,
315291
definitions,
316292
scc_annotations,
317293
member_constraints,
318-
outlives_constraints,
294+
outlives_constraints: Frozen::freeze(outlives_constraints),
319295
type_tests,
320296
liveness_constraints,
321297
universe_causes,
322298
placeholder_indices,
323299
}
324300
}
325301

326-
fn rewrite_outlives<'tcx>(
302+
fn rewrite_placeholder_outlives<'tcx>(
327303
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
328304
annotations: &SccAnnotations<'_, '_, RegionTracker>,
329305
fr_static: RegionVid,
330306
outlives_constraints: &mut OutlivesConstraintSet<'tcx>,
331-
) -> FxHashSet<ConstraintSccIndex> {
332-
// Changed to `true` if we added any constraints to `self` and need to
307+
) -> bool {
308+
// Changed to `true` if we added any constraints and need to
333309
// recompute SCCs.
334-
let mut outlives_static = FxHashSet::default();
310+
let mut added_constraints = false;
335311

336312
let annotations = &annotations.scc_to_annotation;
337313

@@ -354,9 +330,8 @@ fn rewrite_outlives<'tcx>(
354330
// needed for correctness, since an SCC upstream of another with
355331
// a universe violation will "infect" its downstream SCCs to also
356332
// outlive static.
357-
outlives_static.insert(scc);
358333
let scc_representative_outlives_static = OutlivesConstraint {
359-
sup: annotation.representative,
334+
sup: annotation.representative.rvid(),
360335
sub: fr_static,
361336
category: ConstraintCategory::IllegalUniverse,
362337
locations: Locations::All(rustc_span::DUMMY_SP),
@@ -365,7 +340,9 @@ fn rewrite_outlives<'tcx>(
365340
from_closure: false,
366341
};
367342
outlives_constraints.push(scc_representative_outlives_static);
343+
added_constraints = true;
344+
debug!("Added {:?}: 'static!", annotation.representative.rvid());
368345
}
369346
}
370-
outlives_static
347+
added_constraints
371348
}

compiler/rustc_borrowck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ mod constraints;
7575
mod dataflow;
7676
mod def_use;
7777
mod diagnostics;
78-
mod eliminate_placeholders;
78+
mod handle_placeholders;
7979
mod member_constraints;
8080
mod nll;
8181
mod path_utils;

compiler/rustc_borrowck/src/nll.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use tracing::{debug, instrument};
2222
use crate::borrow_set::BorrowSet;
2323
use crate::consumers::ConsumerOptions;
2424
use crate::diagnostics::RegionErrors;
25-
use crate::eliminate_placeholders::rewrite_higher_kinded_outlives_as_constraints;
25+
use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
2626
use crate::polonius::PoloniusDiagnosticsContext;
2727
use crate::polonius::legacy::{
2828
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
@@ -118,7 +118,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
118118
Rc::clone(&location_map),
119119
);
120120

121-
let lowered_constraints = rewrite_higher_kinded_outlives_as_constraints(
121+
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
122122
constraints,
123123
&universal_region_relations,
124124
infcx,

compiler/rustc_borrowck/src/polonius/legacy/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tracing::debug;
1313

1414
use crate::borrow_set::BorrowSet;
1515
use crate::constraints::OutlivesConstraint;
16-
use crate::eliminate_placeholders::LoweredConstraints;
16+
use crate::handle_placeholders::LoweredConstraints;
1717
use crate::type_check::free_region_relations::UniversalRegionRelations;
1818
use crate::universal_regions::UniversalRegions;
1919

compiler/rustc_borrowck/src/region_infer/dump_mir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
4646
"| {r:rw$?} | {ui:4?} | {v}",
4747
r = region,
4848
rw = REGION_WIDTH,
49-
ui = self.region_universe(region),
49+
ui = self.max_nameable_universe(self.constraint_sccs.scc(region)),
5050
v = self.region_value_str(region),
5151
)?;
5252
}

0 commit comments

Comments
 (0)