Skip to content

Commit 222618c

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 698631c commit 222618c

File tree

2 files changed

+135
-131
lines changed

2 files changed

+135
-131
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

+130-129
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ pub(crate) mod values;
4646

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

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

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

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

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

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

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

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

121+
let rvid_is_placeholder = representative_origin == Placeholder;
122+
106123
let placeholder_universe =
107-
if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
124+
if rvid_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
125+
126+
let representative_if_placeholder = if rvid_is_placeholder { Some(rvid) } else { None };
108127

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

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

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

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

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

@@ -411,7 +477,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
411477
member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
412478
universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
413479
type_tests: Vec<TypeTest<'tcx>>,
414-
liveness_constraints: LivenessValues,
480+
mut liveness_constraints: LivenessValues,
415481
elements: &Rc<DenseLocationMap>,
416482
) -> Self {
417483
debug!("universal_regions: {:#?}", universal_regions);
@@ -420,10 +486,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
420486
debug!("type tests: {:#?}", type_tests);
421487

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

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

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

@@ -1637,12 +1635,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
16371635
placeholder: ty::PlaceholderRegion,
16381636
errors_buffer: &mut RegionErrors<'tcx>,
16391637
) {
1640-
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,);
1638+
debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder);
16411639

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

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

compiler/rustc_borrowck/src/region_infer/opaque_types.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
212212

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

0 commit comments

Comments
 (0)