Skip to content

Commit a0193a4

Browse files
committed
Fix a bug (thanks @lcnr!) when a placeholder illegally outlives an existential
1 parent 2acd739 commit a0193a4

File tree

3 files changed

+112
-27
lines changed

3 files changed

+112
-27
lines changed

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,19 @@ pub(crate) enum RegionErrorKind<'tcx> {
161161
/// encountered and leave the rest unreported so as not to overwhelm the user.
162162
is_reported: bool,
163163
},
164+
/// Indicates that a placeholder has a universe too large for one
165+
/// of its member existentials, or, equivalently, that there is
166+
/// a path through the outlives constraint graph from a placeholder
167+
/// to an existential region that cannot name it.
168+
PlaceholderReachesExistentialThatCannotNameIt {
169+
/// the placeholder that transitively outlives an
170+
/// existential that shouldn't leak into it
171+
longer_fr: RegionVid,
172+
/// The existential leaking into `longer_fr`.
173+
existental_that_cannot_name_longer: RegionVid,
174+
// `longer_fr`'s originating placeholder region.
175+
placeholder: ty::PlaceholderRegion,
176+
},
164177
}
165178

166179
/// Information about the various region constraints involved in a borrow checker error.
@@ -349,15 +362,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
349362

350363
for (nll_error, _) in nll_errors.into_iter() {
351364
match nll_error {
352-
// A type-test failed and the constraint was rewritten due
353-
// to higher-ranked trait bounds.
354365
RegionErrorKind::TypeTestError {
355366
generic_kind,
356367
lower_bound,
357368
span,
358369
failed_due_to_placeholders: true,
359370
} => emit_generic_does_not_live_long_enough(self, generic_kind, span, lower_bound),
360-
361371
RegionErrorKind::TypeTestError {
362372
lower_bound,
363373
span,
@@ -396,7 +406,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
396406
lower_bound_region,
397407
));
398408
}
399-
400409
RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
401410
let named_ty =
402411
self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty);
@@ -420,33 +429,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
420429
diag.delay_as_bug();
421430
}
422431
}
423-
424432
RegionErrorKind::BoundUniversalRegionError {
425433
longer_fr,
426434
placeholder,
427435
error_element,
428436
} => {
429437
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
430438

431-
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
432-
let (_, cause) = self.regioncx.find_outlives_blame_span(
439+
self.report_erroneous_rvid_reaches_placeholder(
433440
longer_fr,
434-
NllRegionVariableOrigin::Placeholder(placeholder),
435-
error_vid,
436-
);
437-
438-
// FIXME these methods should have better names, and also probably not be this generic.
439-
// FIXME note that we *throw away* the error element here! We probably want to
440-
// thread it through the computation further down and use it, but there currently isn't
441-
// anything there to receive it.
442-
self.regioncx.universe_info(placeholder.universe).report_erroneous_element(
443-
self,
444441
placeholder,
445-
cause,
446-
None,
442+
error_vid,
447443
);
448444
}
449-
450445
RegionErrorKind::PlaceholderMismatch { rvid_a, rvid_b, origin_a, origin_b } => {
451446
debug!(
452447
"Placeholder mismatch: {rvid_a:?} ({origin_a:?}) reaches {rvid_b:?} ({origin_b:?})"
@@ -468,7 +463,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
468463
Some(origin_b),
469464
);
470465
}
471-
472466
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
473467
if is_reported {
474468
self.report_region_error(
@@ -490,6 +484,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
490484
);
491485
}
492486
}
487+
RegionErrorKind::PlaceholderReachesExistentialThatCannotNameIt {
488+
longer_fr,
489+
existental_that_cannot_name_longer,
490+
placeholder,
491+
} => self.report_erroneous_rvid_reaches_placeholder(
492+
longer_fr,
493+
placeholder,
494+
existental_that_cannot_name_longer,
495+
),
493496
}
494497
}
495498

compiler/rustc_borrowck/src/eliminate_placeholders.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ struct RegionTracker {
112112

113113
// Metadata about reachable placeholders
114114
reachable_placeholders: PlaceholderReachability,
115+
116+
// Track the existential with the smallest universe we reach.
117+
min_universe_reachable_existential: Option<(UniverseIndex, RegionVid)>,
115118
}
116119

117120
impl scc::Annotation for RegionTracker {
@@ -122,14 +125,25 @@ impl scc::Annotation for RegionTracker {
122125
} else {
123126
self.min_universe
124127
};
128+
129+
let min_universe_reachable_existential = smallest_reachable_existential(
130+
self.min_universe_reachable_existential,
131+
other.min_universe_reachable_existential,
132+
);
125133
Self {
126134
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
127135
min_universe,
128136
representative: self.representative.merge_scc(other.representative),
137+
min_universe_reachable_existential,
129138
}
130139
}
131140

132141
fn merge_reached(mut self, other: Self) -> Self {
142+
self.min_universe_reachable_existential = smallest_reachable_existential(
143+
self.min_universe_reachable_existential,
144+
other.min_universe_reachable_existential,
145+
);
146+
133147
// This detail is subtle. We stop early here, because there may be multiple
134148
// illegally reached universes, but they are not equally good as blame candidates.
135149
// In general, the ones with the smallest indices of their RegionVids will
@@ -150,14 +164,25 @@ impl scc::Annotation for RegionTracker {
150164
}
151165
}
152166

167+
fn smallest_reachable_existential(
168+
min_universe_reachable_existential_1: Option<(UniverseIndex, RegionVid)>,
169+
min_universe_reachable_existential_2: Option<(UniverseIndex, RegionVid)>,
170+
) -> Option<(UniverseIndex, RegionVid)> {
171+
match (min_universe_reachable_existential_1, min_universe_reachable_existential_2) {
172+
(Some(a), Some(b)) => Some(std::cmp::min(a, b)),
173+
(a, b) => a.or(b),
174+
}
175+
}
176+
153177
impl RegionTracker {
154178
fn new(representative: RegionVid, definition: &RegionDefinition<'_>) -> Self {
155179
let universe_and_rvid = (definition.universe, representative);
156-
let (representative, reachable_placeholders) = {
180+
let (representative, reachable_placeholders, min_universe_reachable_existential) = {
157181
match definition.origin {
158182
NllRegionVariableOrigin::FreeRegion => (
159183
Representative::FreeRegion(representative),
160184
PlaceholderReachability::NoPlaceholders,
185+
None,
161186
),
162187
NllRegionVariableOrigin::Placeholder(_) => (
163188
Representative::Placeholder(representative),
@@ -166,14 +191,21 @@ impl RegionTracker {
166191
min_placeholder: representative,
167192
max_placeholder: representative,
168193
},
194+
None,
169195
),
170196
NllRegionVariableOrigin::Existential { .. } => (
171197
Representative::Existential(representative),
172198
PlaceholderReachability::NoPlaceholders,
199+
Some((definition.universe, representative)),
173200
),
174201
}
175202
};
176-
Self { representative, min_universe: universe_and_rvid, reachable_placeholders }
203+
Self {
204+
representative,
205+
min_universe: universe_and_rvid,
206+
reachable_placeholders,
207+
min_universe_reachable_existential,
208+
}
177209
}
178210

179211
/// The smallest-indexed universe reachable from and/or in this SCC.
@@ -237,6 +269,16 @@ impl RegionTracker {
237269

238270
Some((max_u_rvid, max_u))
239271
}
272+
273+
/// Check for the second and final type of placeholder leak,
274+
/// where an existential `'e` outlives (transitively) a placeholder `p`
275+
/// and `e` cannot name `p`.
276+
///
277+
/// Returns *a* culprit (though there may be more than one).
278+
fn reaches_existential_that_cannot_name_us(&self) -> Option<RegionVid> {
279+
let (min_u, min_rvid) = self.min_universe_reachable_existential?;
280+
(min_u < self.min_universe()).then_some(min_rvid)
281+
}
240282
}
241283

242284
impl scc::Annotations<RegionVid, ConstraintSccIndex, RegionTracker>
@@ -279,10 +321,19 @@ fn find_placeholder_mismatch_errors<'tcx>(
279321
};
280322

281323
let scc = sccs.scc(rvid);
324+
let annotation = annotations.scc_to_annotation[scc];
282325

283-
let Some(other_placeholder) =
284-
annotations.scc_to_annotation[scc].reaches_other_placeholder(rvid)
285-
else {
326+
if let Some(existental_that_cannot_name_rvid) =
327+
annotation.reaches_existential_that_cannot_name_us()
328+
{
329+
errors_buffer.push(RegionErrorKind::PlaceholderReachesExistentialThatCannotNameIt {
330+
longer_fr: rvid,
331+
existental_that_cannot_name_longer: existental_that_cannot_name_rvid,
332+
placeholder: origin_a,
333+
})
334+
}
335+
336+
let Some(other_placeholder) = annotation.reaches_other_placeholder(rvid) else {
286337
trace!("{rvid:?} reaches no other placeholders");
287338
continue;
288339
};
@@ -486,6 +537,7 @@ fn rewrite_outlives<'tcx>(
486537
for scc in sccs.all_sccs() {
487538
let annotation: RegionTracker = annotations.scc_to_annotation[scc];
488539
if scc == sccs.scc(fr_static) {
540+
trace!("Skipping adding 'static: 'static.");
489541
// No use adding 'static: 'static.
490542
continue;
491543
}
@@ -501,7 +553,10 @@ fn rewrite_outlives<'tcx>(
501553

502554
let min_u = annotation.min_universe();
503555

504-
debug!("Universe {max_u:?} is too large for its SCC!");
556+
debug!(
557+
"Universe {max_u:?} is too large for its SCC, represented by {:?}",
558+
annotation.representative
559+
);
505560
let blame_to = if annotation.representative.rvid() == max_u_rvid {
506561
// We originally had a large enough universe to fit all our reachable
507562
// placeholders, but had it lowered because we also absorbed something

compiler/rustc_borrowck/src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,6 +2502,33 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
25022502
tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
25032503
}
25042504
}
2505+
2506+
/// Report that longer_fr: shorter_fr, which doesn't hold,
2507+
/// where longer_fr is a placeholder from `placeholder`.
2508+
fn report_erroneous_rvid_reaches_placeholder(
2509+
&mut self,
2510+
longer_fr: RegionVid,
2511+
placeholder: ty::Placeholder<ty::BoundRegion>,
2512+
error_vid: RegionVid,
2513+
) {
2514+
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
2515+
let (_, cause) = self.regioncx.find_outlives_blame_span(
2516+
longer_fr,
2517+
NllRegionVariableOrigin::Placeholder(placeholder),
2518+
error_vid,
2519+
);
2520+
2521+
// FIXME these methods should have better names, and also probably not be this generic.
2522+
// FIXME note that we *throw away* the error element here! We probably want to
2523+
// thread it through the computation further down and use it, but there currently isn't
2524+
// anything there to receive it.
2525+
self.regioncx.universe_info(placeholder.universe).report_erroneous_element(
2526+
self,
2527+
placeholder,
2528+
cause,
2529+
None,
2530+
);
2531+
}
25052532
}
25062533

25072534
/// The degree of overlap between 2 places for borrow-checking.

0 commit comments

Comments
 (0)