Skip to content

Commit 6a4deba

Browse files
committed
Handle more placeholder errors.
This update extends the SCC metadata tracking a lot and uses the extra information to add p: 'static for any placeholder that reaches another placeholder. It also inlines the few measly bits of `init_free_and_bound_regions()` that still remain as relevant. This increases the constructor for `RegionInferenceContext`s somewhat, but I still think it's readable. The documentation for `init_free_and_bound_regions()` was out of date, and the correct, up to date version is available in the various places where the logic was moved.
1 parent 359b62c commit 6a4deba

File tree

2 files changed

+135
-131
lines changed

2 files changed

+135
-131
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 130 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ pub(crate) mod values;
4747

4848
pub(crate) type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>;
4949

50+
/// A simpler version of `RegionVariableOrigin` without the
51+
/// metadata.
52+
#[derive(Copy, Debug, Clone, PartialEq)]
53+
enum RepresentativeOrigin {
54+
Existential,
55+
Placeholder,
56+
FreeRegion,
57+
}
5058
/// An annotation for region graph SCCs that tracks
5159
/// the values of its elements.
5260
#[derive(Copy, Debug, Clone)]
@@ -63,71 +71,127 @@ pub struct RegionTracker {
6371
/// it's the one with the smallest Region Variable ID.
6472
pub(crate) representative: RegionVid,
6573

66-
/// Is the current representative a placeholder?
67-
representative_is_placeholder: bool,
74+
/// Where does the representative region variable come from?
75+
representative_origin: RepresentativeOrigin,
76+
77+
/// The smallest reachable placeholder from this SCC (including in it).
78+
min_reachable_placeholder: Option<RegionVid>,
79+
80+
/// The largest reachable placeholder from this SCC (including in it).
81+
max_reachable_placeholder: Option<RegionVid>,
6882

69-
/// Is the current representative existentially quantified?
70-
representative_is_existential: bool,
83+
/// Is there at least one placeholder in this SCC?
84+
contains_placeholder: bool,
7185
}
7286

7387
impl scc::Annotation for RegionTracker {
74-
fn merge_scc(mut self, mut other: Self) -> Self {
75-
// Prefer any placeholder over any existential
76-
if other.representative_is_placeholder && self.representative_is_existential {
77-
other.merge_min_max_seen(&self);
78-
return other;
79-
}
88+
fn merge_scc(self, other: Self) -> Self {
89+
use RepresentativeOrigin::*;
8090

81-
if self.representative_is_placeholder && other.representative_is_existential
82-
|| (self.representative <= other.representative)
91+
let (mut shorter, longer) = match (self.representative_origin, other.representative_origin)
8392
{
84-
self.merge_min_max_seen(&other);
85-
return self;
86-
}
87-
other.merge_min_max_seen(&self);
88-
other
93+
// Prefer any placeholder over any existential
94+
(Existential, Placeholder) => (other, self),
95+
(Placeholder, Existential) => (self, other),
96+
97+
// In any other case, pick the one with the smallest id.
98+
_ if self.representative <= other.representative => (self, other),
99+
_ => (other, self),
100+
};
101+
shorter.contains_placeholder |= longer.contains_placeholder;
102+
shorter.merge_min_max_seen(&longer);
103+
shorter
89104
}
90105

91106
fn merge_reached(mut self, other: Self) -> Self {
92-
// No update to in-component values, only add seen values.
93107
self.merge_min_max_seen(&other);
94108
self
95109
}
96110
}
97111

98112
impl RegionTracker {
99113
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
100-
let (representative_is_placeholder, representative_is_existential) = match definition.origin
101-
{
102-
rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false),
103-
rustc_infer::infer::NllRegionVariableOrigin::Placeholder(_) => (true, false),
104-
rustc_infer::infer::NllRegionVariableOrigin::Existential { .. } => (false, true),
114+
use RepresentativeOrigin::*;
115+
116+
let representative_origin = match definition.origin {
117+
NllRegionVariableOrigin::FreeRegion => FreeRegion,
118+
NllRegionVariableOrigin::Placeholder(_) => Placeholder,
119+
NllRegionVariableOrigin::Existential { .. } => Existential,
105120
};
106121

122+
let rvid_is_placeholder = representative_origin == Placeholder;
123+
107124
let placeholder_universe =
108-
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
125+
if rvid_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
126+
127+
let representative_if_placeholder = if rvid_is_placeholder { Some(rvid) } else { None };
109128

110129
Self {
111130
max_placeholder_universe_reached: placeholder_universe,
112131
min_reachable_universe: definition.universe,
113132
representative: rvid,
114-
representative_is_placeholder,
115-
representative_is_existential,
133+
representative_origin,
134+
min_reachable_placeholder: representative_if_placeholder,
135+
max_reachable_placeholder: representative_if_placeholder,
136+
contains_placeholder: rvid_is_placeholder,
116137
}
117138
}
118139

140+
/// Return true if this SCC contains a placeholder that
141+
/// reaches another placeholder, through other SCCs or within
142+
/// it.
143+
fn placeholder_reaches_placeholder(&self) -> bool {
144+
// If min and max are different then at least two placeholders
145+
// must be reachable from us. It remains to determine if and
146+
// whose problem that is.
147+
//
148+
// If we are not a placeholder
149+
// we are seeing upstream placeholders, which may be fine, or
150+
// if it is a problem it's the problem for other placeholders.
151+
//
152+
// If we *are* a placeholder, we are reaching at least one other
153+
// placeholder upstream.
154+
self.contains_placeholder
155+
&& self.min_reachable_placeholder != self.max_reachable_placeholder
156+
}
157+
119158
/// If the representative is a placeholder, return it,
120159
/// otherwise return None.
121160
fn placeholder_representative(&self) -> Option<RegionVid> {
122-
if self.representative_is_placeholder { Some(self.representative) } else { None }
161+
if self.representative_origin == RepresentativeOrigin::Placeholder {
162+
Some(self.representative)
163+
} else {
164+
None
165+
}
123166
}
124167

125168
/// The smallest-indexed universe reachable from and/or in this SCC.
126169
fn min_universe(self) -> UniverseIndex {
127170
self.min_reachable_universe
128171
}
129172

173+
fn merge_reachable_placeholders(&mut self, other: &Self) {
174+
// The largest reachable placeholder, or None if neither reaches any.
175+
// This works because None is smaller than any Some.
176+
let max_max = self.max_reachable_placeholder.max(other.max_reachable_placeholder);
177+
178+
// Neither reach a placeholder
179+
if max_max.is_none() {
180+
return;
181+
}
182+
183+
self.max_reachable_placeholder = max_max;
184+
185+
// If the smallest one is None, pick the largest Option; the single Some.
186+
self.min_reachable_placeholder = self
187+
.min_reachable_placeholder
188+
.min(other.min_reachable_placeholder)
189+
.or_else(|| self.min_reachable_placeholder.max(other.min_reachable_placeholder));
190+
}
191+
130192
fn merge_min_max_seen(&mut self, other: &Self) {
193+
self.merge_reachable_placeholders(other);
194+
131195
self.max_placeholder_universe_reached = std::cmp::max(
132196
self.max_placeholder_universe_reached,
133197
other.max_placeholder_universe_reached,
@@ -137,10 +201,12 @@ impl RegionTracker {
137201
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
138202
}
139203

140-
/// Returns `true` if during the annotated SCC reaches a placeholder
141-
/// with a universe larger than the smallest reachable one, `false` otherwise.
204+
/// Returns `true` if the annotated SCC reaches a placeholder
205+
/// with a universe larger than the smallest reachable one,
206+
/// or if a placeholder reaches another placeholder, `false` otherwise.
142207
pub(crate) fn has_incompatible_universes(&self) -> bool {
143208
self.min_universe().cannot_name(self.max_placeholder_universe_reached)
209+
|| self.placeholder_reaches_placeholder()
144210
}
145211
}
146212

@@ -412,7 +478,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
412478
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
413479
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
414480
type_tests: Vec<TypeTest<'tcx>>,
415-
liveness_constraints: LivenessValues,
481+
mut liveness_constraints: LivenessValues,
416482
elements: &Rc<DenseLocationMap>,
417483
) -> Self {
418484
debug!("universal_regions: {:#?}", universal_regions);
@@ -421,10 +487,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
421487
debug!("type tests: {:#?}", type_tests);
422488

423489
// Create a RegionDefinition for each inference variable.
424-
let definitions: IndexVec<_, _> = var_infos
425-
.iter()
426-
.map(|info| RegionDefinition::new(info.universe, info.origin))
427-
.collect();
490+
let definitions = {
491+
let mut definitions: IndexVec<_, _> = var_infos
492+
.iter()
493+
.map(|info| RegionDefinition::new(info.universe, info.origin))
494+
.collect();
495+
496+
// Add external names from universal regions in fun function definitions.
497+
for (external_name, variable) in universal_regions.named_universal_regions() {
498+
debug!("region {:?} has external name {:?}", variable, external_name);
499+
definitions[variable].external_name = Some(external_name);
500+
}
501+
definitions
502+
};
428503

429504
let constraint_sccs =
430505
outlives_constraints.add_outlives_static(&universal_regions, &definitions);
@@ -445,7 +520,23 @@ impl<'tcx> RegionInferenceContext<'tcx> {
445520
let member_constraints =
446521
Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
447522

448-
let mut result = Self {
523+
// Initialise free, universally quantified regions to be live at all points.
524+
for variable in definitions.indices() {
525+
if let NllRegionVariableOrigin::FreeRegion = definitions[variable].origin {
526+
// For each free, universally quantified region X:
527+
528+
let scc = constraint_sccs.scc(variable);
529+
530+
// Add all nodes in the CFG to liveness constraints
531+
liveness_constraints.add_all_points(variable);
532+
scc_values.add_all_points(scc);
533+
534+
// Add `end(X)` into the set for X.
535+
scc_values.add_element(scc, variable);
536+
}
537+
}
538+
539+
Self {
449540
var_infos,
450541
definitions,
451542
liveness_constraints,
@@ -460,99 +551,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
460551
type_tests,
461552
universal_regions,
462553
universal_region_relations,
463-
};
464-
465-
result.init_free_and_bound_regions();
466-
467-
result
468-
}
469-
470-
/// Initializes the region variables for each universally
471-
/// quantified region (lifetime parameter). The first N variables
472-
/// always correspond to the regions appearing in the function
473-
/// signature (both named and anonymous) and where-clauses. This
474-
/// function iterates over those regions and initializes them with
475-
/// minimum values.
476-
///
477-
/// For example:
478-
/// ```
479-
/// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ }
480-
/// ```
481-
/// would initialize two variables like so:
482-
/// ```ignore (illustrative)
483-
/// R0 = { CFG, R0 } // 'a
484-
/// R1 = { CFG, R0, R1 } // 'b
485-
/// ```
486-
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
487-
/// and (b) any universally quantified regions that it outlives,
488-
/// which in this case is just itself. R1 (`'b`) in contrast also
489-
/// outlives `'a` and hence contains R0 and R1.
490-
///
491-
/// This bit of logic also handles invalid universe relations
492-
/// for higher-kinded types.
493-
///
494-
/// We Walk each SCC `A` and `B` such that `A: B`
495-
/// and ensure that universe(A) can see universe(B).
496-
///
497-
/// This serves to enforce the 'empty/placeholder' hierarchy
498-
/// (described in more detail on `RegionKind`):
499-
///
500-
/// ```ignore (illustrative)
501-
/// static -----+
502-
/// | |
503-
/// empty(U0) placeholder(U1)
504-
/// | /
505-
/// empty(U1)
506-
/// ```
507-
///
508-
/// In particular, imagine we have variables R0 in U0 and R1
509-
/// created in U1, and constraints like this;
510-
///
511-
/// ```ignore (illustrative)
512-
/// R1: !1 // R1 outlives the placeholder in U1
513-
/// R1: R0 // R1 outlives R0
514-
/// ```
515-
///
516-
/// Here, we wish for R1 to be `'static`, because it
517-
/// cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
518-
///
519-
/// Thanks to this loop, what happens is that the `R1: R0`
520-
/// constraint has lowered the universe of `R1` to `U0`, which in turn
521-
/// means that the `R1: !1` constraint here will cause
522-
/// `R1` to become `'static`.
523-
fn init_free_and_bound_regions(&mut self) {
524-
// Update the names (if any)
525-
// This iterator has unstable order but we collect it all into an IndexVec
526-
for (external_name, variable) in self.universal_regions.named_universal_regions() {
527-
debug!(
528-
"init_free_and_bound_regions: region {:?} has external name {:?}",
529-
variable, external_name
530-
);
531-
self.definitions[variable].external_name = Some(external_name);
532-
}
533-
534-
for variable in self.definitions.indices() {
535-
let scc = self.constraint_sccs.scc(variable);
536-
537-
match self.definitions[variable].origin {
538-
NllRegionVariableOrigin::FreeRegion => {
539-
// For each free, universally quantified region X:
540-
541-
// Add all nodes in the CFG to liveness constraints
542-
self.liveness_constraints.add_all_points(variable);
543-
self.scc_values.add_all_points(scc);
544-
545-
// Add `end(X)` into the set for X.
546-
self.scc_values.add_element(scc, variable);
547-
}
548-
549-
NllRegionVariableOrigin::Placeholder { .. } => {
550-
// Placeholders are already handled by rewriting constraints.
551-
}
552-
NllRegionVariableOrigin::Existential { .. } => {
553-
// For existential, regions, nothing to do.
554-
}
555-
}
556554
}
557555
}
558556

@@ -1638,12 +1636,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
16381636
placeholder: ty::PlaceholderRegion,
16391637
errors_buffer: &mut RegionErrors<'tcx>,
16401638
) {
1641-
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,);
1639+
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder);
16421640

16431641
let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
16441642
debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,);
16451643

16461644
for error_element in self.scc_values.elements_contained_in(longer_fr_scc) {
1645+
debug!(
1646+
"check_bound_universal_region, error_element: {error_element:?} for placeholder {placeholder:?} in scc: {longer_fr_scc:?}"
1647+
);
16471648
match error_element {
16481649
RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {}
16491650
}

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
213213

214214
// Special handling of higher-ranked regions.
215215
if !self.scc_universe(scc).is_root() {
216-
let annotation = self.constraint_sccs.annotation(scc);
217-
if annotation.representative_is_placeholder && vid == annotation.representative
216+
if self
217+
.constraint_sccs
218+
.annotation(scc)
219+
.placeholder_representative()
220+
.is_some_and(|scc_placeholder| vid == scc_placeholder)
218221
{
219222
// FIXME: somehow construct the right type out of the representative!
220223
return region;

0 commit comments

Comments
 (0)