Skip to content

Commit 36a6ed9

Browse files
committed
Move handling of placeholder errors to before region inference
1 parent 5e33838 commit 36a6ed9

File tree

6 files changed

+243
-33
lines changed

6 files changed

+243
-33
lines changed

compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_
2424
use tracing::{debug, instrument};
2525

2626
use crate::MirBorrowckCtxt;
27-
use crate::region_infer::values::RegionElement;
2827
use crate::session_diagnostics::{
2928
HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError,
3029
};
@@ -49,11 +48,12 @@ impl<'tcx> UniverseInfo<'tcx> {
4948
UniverseInfo::RelateTys { expected, found }
5049
}
5150

51+
/// Report an error where an element erroneously made its way into `placeholder`.
5252
pub(crate) fn report_erroneous_element(
5353
&self,
5454
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
5555
placeholder: ty::PlaceholderRegion,
56-
error_element: RegionElement,
56+
error_element: Option<ty::PlaceholderRegion>,
5757
cause: ObligationCause<'tcx>,
5858
) {
5959
match *self {
@@ -153,10 +153,17 @@ pub(crate) trait TypeOpInfo<'tcx> {
153153
&self,
154154
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
155155
placeholder: ty::PlaceholderRegion,
156-
error_element: RegionElement,
156+
error_element: Option<ty::PlaceholderRegion>,
157157
cause: ObligationCause<'tcx>,
158158
) {
159159
let tcx = mbcx.infcx.tcx;
160+
161+
// FIXME: this logic is convoluted and strange, and
162+
// we should probably just use the placeholders we get
163+
// as arguments! However, upstream error reporting code
164+
// needs adaptations for that to work (this universe is
165+
// neither the assigned one, nor the calculated one but
166+
// some base-shifted one for some reason?).
160167
let base_universe = self.base_universe();
161168
debug!(?base_universe);
162169

@@ -172,20 +179,16 @@ pub(crate) trait TypeOpInfo<'tcx> {
172179
ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
173180
);
174181

175-
let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) =
176-
error_element
177-
{
178-
let adjusted_universe =
179-
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
182+
// FIXME: one day this should just be error_element, but see above about the adjusted universes. At that point, this method can probably be removed entirely.
183+
let error_region = error_element.and_then(|e| {
184+
let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32());
180185
adjusted_universe.map(|adjusted| {
181186
ty::Region::new_placeholder(
182187
tcx,
183-
ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
188+
ty::Placeholder { universe: adjusted.into(), bound: e.bound },
184189
)
185190
})
186-
} else {
187-
None
188-
};
191+
});
189192

190193
debug!(?placeholder_region);
191194

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,31 @@ pub(crate) enum RegionErrorKind<'tcx> {
104104
/// A generic bound failure for a type test (`T: 'a`).
105105
TypeTestError { type_test: TypeTest<'tcx> },
106106

107-
/// Higher-ranked subtyping error.
108-
BoundUniversalRegionError {
107+
/// 'a outlives 'b, and both are placeholders.
108+
PlaceholderOutlivesPlaceholder {
109+
rvid_a: RegionVid,
110+
rvid_b: RegionVid,
111+
origin_a: ty::PlaceholderRegion,
112+
origin_b: ty::PlaceholderRegion,
113+
},
114+
115+
/// Indicates that a placeholder has a universe too large for one
116+
/// of its member existentials, or, equivalently, that there is
117+
/// a path through the outlives constraint graph from a placeholder
118+
/// to an existential region that cannot name it.
119+
PlaceholderOutlivesExistentialThatCannotNameIt {
120+
/// the placeholder that transitively outlives an
121+
/// existential that shouldn't leak into it
122+
longer_fr: RegionVid,
123+
/// The existential leaking into `longer_fr`.
124+
existential_that_cannot_name_longer: RegionVid,
125+
// `longer_fr`'s originating placeholder region.
126+
placeholder: ty::PlaceholderRegion,
127+
},
128+
129+
/// Higher-ranked subtyping error. A placeholder outlives
130+
/// either a location or a universal region.
131+
PlaceholderOutlivesLocationOrUniversal {
109132
/// The placeholder free region.
110133
longer_fr: RegionVid,
111134
/// The region element that erroneously must be outlived by `longer_fr`.
@@ -361,30 +384,56 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
361384
}
362385
}
363386

364-
RegionErrorKind::BoundUniversalRegionError {
387+
RegionErrorKind::PlaceholderOutlivesLocationOrUniversal {
365388
longer_fr,
366389
placeholder,
367390
error_element,
391+
} => self.report_erroneous_rvid_reaches_placeholder(
392+
longer_fr,
393+
placeholder,
394+
self.regioncx.region_from_element(longer_fr, &error_element),
395+
),
396+
RegionErrorKind::PlaceholderOutlivesPlaceholder {
397+
rvid_a,
398+
rvid_b,
399+
origin_a,
400+
origin_b,
368401
} => {
369-
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
402+
debug!(
403+
"Placeholder mismatch: {rvid_a:?} ({origin_a:?}) reaches {rvid_b:?} ({origin_b:?})"
404+
);
370405

371-
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
372406
let cause = self
373407
.regioncx
374408
.best_blame_constraint(
375-
longer_fr,
376-
NllRegionVariableOrigin::Placeholder(placeholder),
377-
error_vid,
409+
rvid_a,
410+
NllRegionVariableOrigin::Placeholder(origin_a),
411+
rvid_b,
378412
)
379413
.0
380414
.cause;
381415

382-
let universe = placeholder.universe;
383-
let universe_info = self.regioncx.universe_info(universe);
384-
385-
universe_info.report_erroneous_element(self, placeholder, error_element, cause);
416+
// FIXME We may be able to shorten the code path here, and immediately
417+
// report a `RegionResolutionError::UpperBoundUniverseConflict`, but
418+
// that's left for a future refactoring.
419+
self.regioncx.universe_info(origin_a.universe).report_erroneous_element(
420+
self,
421+
origin_a,
422+
Some(origin_b),
423+
cause,
424+
);
386425
}
387426

427+
RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt {
428+
longer_fr,
429+
existential_that_cannot_name_longer,
430+
placeholder,
431+
} => self.report_erroneous_rvid_reaches_placeholder(
432+
longer_fr,
433+
placeholder,
434+
existential_that_cannot_name_longer,
435+
),
436+
388437
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
389438
if is_reported {
390439
self.report_region_error(

compiler/rustc_borrowck/src/handle_placeholders.rs

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ use rustc_index::IndexVec;
99
use rustc_infer::infer::RegionVariableOrigin;
1010
use rustc_middle::mir::ConstraintCategory;
1111
use rustc_middle::ty::{RegionVid, UniverseIndex};
12-
use tracing::{debug, trace};
12+
use tracing::{debug, instrument, trace};
1313

1414
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
1515
use crate::consumers::OutlivesConstraint;
16-
use crate::diagnostics::UniverseInfo;
16+
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
1717
use crate::region_infer::values::{LivenessValues, PlaceholderIndices};
1818
use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest};
1919
use crate::ty::VarianceDiagInfo;
@@ -181,6 +181,43 @@ impl RegionTracker {
181181
!self.max_nameable_universe().can_name(placeholder_universe)
182182
})
183183
}
184+
185+
/// Check for the second and final type of placeholder leak,
186+
/// where a placeholder `'p` outlives (transitively) an existential `'e`
187+
/// and `'e` cannot name `'p`. This is sort of a dual of `unnameable_placeholder`;
188+
/// one of the members of this SCC cannot be named by the SCC.
189+
///
190+
/// Returns *a* culprit (though there may be more than one).
191+
fn reaches_existential_that_cannot_name_us(&self) -> Option<RegionVid> {
192+
let Representative::Placeholder(_p) = self.representative else {
193+
return None;
194+
};
195+
196+
let (reachable_lowest_max_u, reachable_lowest_max_u_rvid) = self.max_nameable_universe;
197+
198+
(!self.reachable_placeholders.can_be_named_by(reachable_lowest_max_u))
199+
.then_some(reachable_lowest_max_u_rvid)
200+
}
201+
202+
/// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`,
203+
/// returning it if that is the case. This prefers the placeholder with the
204+
/// smallest region variable ID.
205+
fn reaches_other_placeholder(&self, placeholder_rvid: RegionVid) -> Option<RegionVid> {
206+
match self.reachable_placeholders {
207+
PlaceholderReachability::NoPlaceholders => None,
208+
PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. }
209+
if min_placeholder == max_placeholder =>
210+
{
211+
None
212+
}
213+
PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. }
214+
if min_placeholder == placeholder_rvid =>
215+
{
216+
Some(max_placeholder)
217+
}
218+
PlaceholderReachability::Placeholders { min_placeholder, .. } => Some(min_placeholder),
219+
}
220+
}
184221
}
185222

186223
impl scc::Annotation for RegionTracker {
@@ -270,6 +307,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
270307
constraints: MirTypeckRegionConstraints<'tcx>,
271308
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
272309
infcx: &BorrowckInferCtxt<'tcx>,
310+
errors_buffer: &mut RegionErrors<'tcx>,
273311
) -> LoweredConstraints<'tcx> {
274312
let universal_regions = &universal_region_relations.universal_regions;
275313
let (definitions, has_placeholders) = region_definitions(infcx, universal_regions);
@@ -322,6 +360,13 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>(
322360
&mut outlives_constraints,
323361
);
324362

363+
find_placeholder_mismatch_errors(
364+
&definitions,
365+
&constraint_sccs,
366+
&scc_annotations,
367+
errors_buffer,
368+
);
369+
325370
let (constraint_sccs, scc_annotations) = if added_constraints {
326371
let mut annotations = SccAnnotations::init(&definitions);
327372

@@ -419,3 +464,65 @@ fn rewrite_placeholder_outlives<'tcx>(
419464
}
420465
added_constraints
421466
}
467+
468+
/// Identify errors where placeholders illegally reach other regions, and generate
469+
/// errors stored into `errors_buffer`.
470+
///
471+
/// There are two sources of such errors:
472+
/// 1. A placeholder reaches (possibly transitively) another placeholder.
473+
/// 2. A placeholder `p` reaches (possibly transitively) an existential `e`,
474+
/// where `e` has an allowed maximum universe smaller than `p`'s.
475+
///
476+
/// There are other potential placeholder errors, but those are detected after
477+
/// region inference, since it may apply type tests or member constraints that
478+
/// alter the contents of SCCs and thus can't be detected at this point.
479+
#[instrument(skip(definitions, sccs, annotations, errors_buffer), level = "debug")]
480+
fn find_placeholder_mismatch_errors<'tcx>(
481+
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
482+
sccs: &Sccs<RegionVid, ConstraintSccIndex>,
483+
annotations: &SccAnnotations<'_, '_, RegionTracker>,
484+
errors_buffer: &mut RegionErrors<'tcx>,
485+
) {
486+
use NllRegionVariableOrigin::Placeholder;
487+
for (rvid, definition) in definitions.iter_enumerated() {
488+
let Placeholder(origin_a) = definition.origin else {
489+
continue;
490+
};
491+
492+
let scc = sccs.scc(rvid);
493+
let annotation = annotations.scc_to_annotation[scc];
494+
495+
if let Some(existential_that_cannot_name_rvid) =
496+
annotation.reaches_existential_that_cannot_name_us()
497+
{
498+
errors_buffer.push(RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt {
499+
longer_fr: rvid,
500+
existential_that_cannot_name_longer: existential_that_cannot_name_rvid,
501+
placeholder: origin_a,
502+
})
503+
}
504+
505+
let Some(other_placeholder) = annotation.reaches_other_placeholder(rvid) else {
506+
trace!("{rvid:?} reaches no other placeholders");
507+
continue;
508+
};
509+
510+
debug!(
511+
"Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}"
512+
);
513+
514+
// FIXME SURELY there is a neater way to do this?
515+
let Placeholder(origin_b) = definitions[other_placeholder].origin else {
516+
unreachable!(
517+
"Region {rvid:?}, {other_placeholder:?} should be placeholders but aren't!"
518+
);
519+
};
520+
521+
errors_buffer.push(RegionErrorKind::PlaceholderOutlivesPlaceholder {
522+
rvid_a: rvid,
523+
rvid_b: other_placeholder,
524+
origin_a,
525+
origin_b,
526+
});
527+
}
528+
}

compiler/rustc_borrowck/src/lib.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,6 +2707,37 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
27072707
tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
27082708
}
27092709
}
2710+
2711+
/// Report that longer_fr: shorter_fr, which doesn't hold,
2712+
/// where longer_fr is a placeholder from `placeholder`.
2713+
fn report_erroneous_rvid_reaches_placeholder(
2714+
&mut self,
2715+
longer_fr: RegionVid,
2716+
placeholder: ty::Placeholder<ty::BoundRegion>,
2717+
error_vid: RegionVid,
2718+
) {
2719+
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
2720+
let cause = self
2721+
.regioncx
2722+
.best_blame_constraint(
2723+
longer_fr,
2724+
NllRegionVariableOrigin::Placeholder(placeholder),
2725+
error_vid,
2726+
)
2727+
.0
2728+
.cause;
2729+
2730+
// FIXME these methods should have better names, and also probably not be this generic.
2731+
// FIXME note that we *throw away* the error element here! We probably want to
2732+
// thread it through the computation further down and use it, but there currently isn't
2733+
// anything there to receive it.
2734+
self.regioncx.universe_info(placeholder.universe).report_erroneous_element(
2735+
self,
2736+
placeholder,
2737+
None,
2738+
cause,
2739+
);
2740+
}
27102741
}
27112742

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

compiler/rustc_borrowck/src/nll.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ pub(crate) fn compute_regions<'tcx>(
126126
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
127127
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
128128

129+
let mut placeholder_errors = RegionErrors::new(infcx.tcx);
130+
129131
let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
130132
constraints,
131133
&universal_region_relations,
132134
infcx,
135+
&mut placeholder_errors,
133136
);
134137

135138
// If requested, emit legacy polonius facts.
@@ -179,9 +182,17 @@ pub(crate) fn compute_regions<'tcx>(
179182
});
180183

181184
// Solve the region constraints.
182-
let (closure_region_requirements, nll_errors) =
185+
let (closure_region_requirements, region_inference_errors) =
183186
regioncx.solve(infcx, body, polonius_output.clone());
184187

188+
let nll_errors = if region_inference_errors.is_empty() {
189+
// Only flag the higher-kinded bounds errors if there are no borrowck errors.
190+
placeholder_errors
191+
} else {
192+
debug!("Errors already reported, skipping these: {placeholder_errors:?}");
193+
region_inference_errors
194+
};
195+
185196
NllOutput {
186197
regioncx,
187198
polonius_input: polonius_facts.map(Box::new),

0 commit comments

Comments
 (0)