From 93fc94672696628fa3f07bb0ce21e730206de91c Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 3 Sep 2025 11:16:25 +0200 Subject: [PATCH 01/17] Move handling of placeholder errors to before region inference --- .../src/diagnostics/bound_region_errors.rs | 27 +++-- .../src/diagnostics/region_errors.rs | 73 ++++++++++-- .../rustc_borrowck/src/handle_placeholders.rs | 111 +++++++++++++++++- compiler/rustc_borrowck/src/lib.rs | 31 +++++ compiler/rustc_borrowck/src/nll.rs | 14 ++- .../rustc_borrowck/src/region_infer/mod.rs | 21 +++- 6 files changed, 244 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 15b2a5ef2e213..a873e04756d06 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_ use tracing::{debug, instrument}; use crate::MirBorrowckCtxt; -use crate::region_infer::values::RegionElement; use crate::session_diagnostics::{ HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError, }; @@ -49,11 +48,12 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfo::RelateTys { expected, found } } + /// Report an error where an element erroneously made its way into `placeholder`. pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, - error_element: RegionElement, + error_element: Option, cause: ObligationCause<'tcx>, ) { match *self { @@ -153,10 +153,17 @@ pub(crate) trait TypeOpInfo<'tcx> { &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, - error_element: RegionElement, + error_element: Option, cause: ObligationCause<'tcx>, ) { let tcx = mbcx.infcx.tcx; + + // FIXME: this logic is convoluted and strange, and + // we should probably just use the placeholders we get + // as arguments! However, upstream error reporting code + // needs adaptations for that to work (this universe is + // neither the assigned one, nor the calculated one but + // some base-shifted one for some reason?). let base_universe = self.base_universe(); debug!(?base_universe); @@ -172,20 +179,16 @@ pub(crate) trait TypeOpInfo<'tcx> { ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound }, ); - let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) = - error_element - { - let adjusted_universe = - error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32()); + // 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. + let error_region = error_element.and_then(|e| { + let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32()); adjusted_universe.map(|adjusted| { ty::Region::new_placeholder( tcx, - ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound }, + ty::Placeholder { universe: adjusted.into(), bound: e.bound }, ) }) - } else { - None - }; + }); debug!(?placeholder_region); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 05826bea66bf8..982a26ef800e6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -104,8 +104,31 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// Higher-ranked subtyping error. - BoundUniversalRegionError { + /// 'a outlives 'b, and both are placeholders. + PlaceholderOutlivesPlaceholder { + rvid_a: RegionVid, + rvid_b: RegionVid, + origin_a: ty::PlaceholderRegion, + origin_b: ty::PlaceholderRegion, + }, + + /// Indicates that a placeholder has a universe too large for one + /// of its member existentials, or, equivalently, that there is + /// a path through the outlives constraint graph from a placeholder + /// to an existential region that cannot name it. + PlaceholderOutlivesExistentialThatCannotNameIt { + /// the placeholder that transitively outlives an + /// existential that shouldn't leak into it + longer_fr: RegionVid, + /// The existential leaking into `longer_fr`. + existential_that_cannot_name_longer: RegionVid, + // `longer_fr`'s originating placeholder region. + placeholder: ty::PlaceholderRegion, + }, + + /// Higher-ranked subtyping error. A placeholder outlives + /// either a location or a universal region. + PlaceholderOutlivesLocationOrUniversal { /// The placeholder free region. longer_fr: RegionVid, /// The region element that erroneously must be outlived by `longer_fr`. @@ -361,30 +384,56 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - RegionErrorKind::BoundUniversalRegionError { + RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { longer_fr, placeholder, error_element, + } => self.report_erroneous_rvid_reaches_placeholder( + longer_fr, + placeholder, + self.regioncx.region_from_element(longer_fr, &error_element), + ), + RegionErrorKind::PlaceholderOutlivesPlaceholder { + rvid_a, + rvid_b, + origin_a, + origin_b, } => { - let error_vid = self.regioncx.region_from_element(longer_fr, &error_element); + debug!( + "Placeholder mismatch: {rvid_a:?} ({origin_a:?}) reaches {rvid_b:?} ({origin_b:?})" + ); - // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. let cause = self .regioncx .best_blame_constraint( - longer_fr, - NllRegionVariableOrigin::Placeholder(placeholder), - error_vid, + rvid_a, + NllRegionVariableOrigin::Placeholder(origin_a), + rvid_b, ) .0 .cause; - let universe = placeholder.universe; - let universe_info = self.regioncx.universe_info(universe); - - universe_info.report_erroneous_element(self, placeholder, error_element, cause); + // FIXME We may be able to shorten the code path here, and immediately + // report a `RegionResolutionError::UpperBoundUniverseConflict`, but + // that's left for a future refactoring. + self.regioncx.universe_info(origin_a.universe).report_erroneous_element( + self, + origin_a, + Some(origin_b), + cause, + ); } + RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt { + longer_fr, + existential_that_cannot_name_longer, + placeholder, + } => self.report_erroneous_rvid_reaches_placeholder( + longer_fr, + placeholder, + existential_that_cannot_name_longer, + ), + RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { if is_reported { self.report_region_error( diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index d23ecf6c70796..1ba1d5ca3e384 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -9,11 +9,11 @@ use rustc_index::IndexVec; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, UniverseIndex}; -use tracing::{debug, trace}; +use tracing::{debug, instrument, trace}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet}; use crate::consumers::OutlivesConstraint; -use crate::diagnostics::UniverseInfo; +use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::region_infer::values::{LivenessValues, PlaceholderIndices}; use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest}; use crate::ty::VarianceDiagInfo; @@ -177,6 +177,43 @@ impl RegionTracker { !self.max_nameable_universe().can_name(placeholder_universe) }) } + + /// Check for the second and final type of placeholder leak, + /// where a placeholder `'p` outlives (transitively) an existential `'e` + /// and `'e` cannot name `'p`. This is sort of a dual of `unnameable_placeholder`; + /// one of the members of this SCC cannot be named by the SCC. + /// + /// Returns *a* culprit (though there may be more than one). + fn reaches_existential_that_cannot_name_us(&self) -> Option { + let Representative::Placeholder(_p) = self.representative else { + return None; + }; + + let (reachable_lowest_max_u, reachable_lowest_max_u_rvid) = self.max_nameable_universe; + + (!self.reachable_placeholders.can_be_named_by(reachable_lowest_max_u)) + .then_some(reachable_lowest_max_u_rvid) + } + + /// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`, + /// returning it if that is the case. This prefers the placeholder with the + /// smallest region variable ID. + fn reaches_other_placeholder(&self, placeholder_rvid: RegionVid) -> Option { + match self.reachable_placeholders { + PlaceholderReachability::NoPlaceholders => None, + PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. } + if min_placeholder == max_placeholder => + { + None + } + PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. } + if min_placeholder == placeholder_rvid => + { + Some(max_placeholder) + } + PlaceholderReachability::Placeholders { min_placeholder, .. } => Some(min_placeholder), + } + } } impl scc::Annotation for RegionTracker { @@ -266,6 +303,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( constraints: MirTypeckRegionConstraints<'tcx>, universal_region_relations: &Frozen>, infcx: &BorrowckInferCtxt<'tcx>, + errors_buffer: &mut RegionErrors<'tcx>, ) -> LoweredConstraints<'tcx> { let universal_regions = &universal_region_relations.universal_regions; let (definitions, has_placeholders) = region_definitions(infcx, universal_regions); @@ -318,6 +356,13 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( &mut outlives_constraints, ); + find_placeholder_mismatch_errors( + &definitions, + &constraint_sccs, + &scc_annotations, + errors_buffer, + ); + let (constraint_sccs, scc_annotations) = if added_constraints { let mut annotations = SccAnnotations::init(&definitions); @@ -415,3 +460,65 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( } added_constraints } + +/// Identify errors where placeholders illegally reach other regions, and generate +/// errors stored into `errors_buffer`. +/// +/// There are two sources of such errors: +/// 1. A placeholder reaches (possibly transitively) another placeholder. +/// 2. A placeholder `p` reaches (possibly transitively) an existential `e`, +/// where `e` has an allowed maximum universe smaller than `p`'s. +/// +/// There are other potential placeholder errors, but those are detected after +/// region inference, since it may apply type tests or member constraints that +/// alter the contents of SCCs and thus can't be detected at this point. +#[instrument(skip(definitions, sccs, annotations, errors_buffer), level = "debug")] +fn find_placeholder_mismatch_errors<'tcx>( + definitions: &IndexVec>, + sccs: &Sccs, + annotations: &SccAnnotations<'_, '_, RegionTracker>, + errors_buffer: &mut RegionErrors<'tcx>, +) { + use NllRegionVariableOrigin::Placeholder; + for (rvid, definition) in definitions.iter_enumerated() { + let Placeholder(origin_a) = definition.origin else { + continue; + }; + + let scc = sccs.scc(rvid); + let annotation = annotations.scc_to_annotation[scc]; + + if let Some(existential_that_cannot_name_rvid) = + annotation.reaches_existential_that_cannot_name_us() + { + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt { + longer_fr: rvid, + existential_that_cannot_name_longer: existential_that_cannot_name_rvid, + placeholder: origin_a, + }) + } + + let Some(other_placeholder) = annotation.reaches_other_placeholder(rvid) else { + trace!("{rvid:?} reaches no other placeholders"); + continue; + }; + + debug!( + "Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}" + ); + + // FIXME SURELY there is a neater way to do this? + let Placeholder(origin_b) = definitions[other_placeholder].origin else { + unreachable!( + "Region {rvid:?}, {other_placeholder:?} should be placeholders but aren't!" + ); + }; + + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesPlaceholder { + rvid_a: rvid, + rvid_b: other_placeholder, + origin_a, + origin_b, + }); + } +} diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 20411fcc16fbb..5017de8148b92 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2691,6 +2691,37 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span }) } } + + /// Report that longer_fr: shorter_fr, which doesn't hold, + /// where longer_fr is a placeholder from `placeholder`. + fn report_erroneous_rvid_reaches_placeholder( + &mut self, + longer_fr: RegionVid, + placeholder: ty::Placeholder, + error_vid: RegionVid, + ) { + // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. + let cause = self + .regioncx + .best_blame_constraint( + longer_fr, + NllRegionVariableOrigin::Placeholder(placeholder), + error_vid, + ) + .0 + .cause; + + // FIXME these methods should have better names, and also probably not be this generic. + // FIXME note that we *throw away* the error element here! We probably want to + // thread it through the computation further down and use it, but there currently isn't + // anything there to receive it. + self.regioncx.universe_info(placeholder.universe).report_erroneous_element( + self, + placeholder, + None, + cause, + ); + } } /// The degree of overlap between 2 places for borrow-checking. diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 5537d90e297aa..ce5be74f8a59e 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -95,6 +95,7 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( constraints.clone(), &universal_region_relations, infcx, + &mut RegionErrors::new(infcx.tcx), ); let mut regioncx = RegionInferenceContext::new( &infcx, @@ -126,10 +127,13 @@ pub(crate) fn compute_regions<'tcx>( let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output()) || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); + let mut placeholder_errors = RegionErrors::new(infcx.tcx); + let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( constraints, &universal_region_relations, infcx, + &mut placeholder_errors, ); // If requested, emit legacy polonius facts. @@ -179,9 +183,17 @@ pub(crate) fn compute_regions<'tcx>( }); // Solve the region constraints. - let (closure_region_requirements, nll_errors) = + let (closure_region_requirements, region_inference_errors) = regioncx.solve(infcx, body, polonius_output.clone()); + let nll_errors = if region_inference_errors.is_empty() { + // Only flag the higher-kinded bounds errors if there are no borrowck errors. + placeholder_errors + } else { + debug!("Errors already reported, skipping these: {placeholder_errors:?}"); + region_inference_errors + }; + NllOutput { regioncx, polonius_input: polonius_facts.map(Box::new), diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index e98c60e633805..97edf212f79a9 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1319,16 +1319,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); + // FIXME(amandasystems): This is an inlined version of elements_contained_in, without + // placeholders, which are handled separately. Later, when placeholders are removed + // from scc_values, this part will just be elements_contained_in(): + let mut non_placeholder_regions_in = self + .scc_values + .locations_outlived_by(longer_fr_scc) + .map(RegionElement::Location) + .chain( + self.scc_values + .universal_regions_outlived_by(longer_fr_scc) + .map(RegionElement::RootUniversalRegion), + ); + // If we have some bound universal region `'a`, then the only // elements it can contain is itself -- we don't know anything // else about it! - if let Some(error_element) = self - .scc_values - .elements_contained_in(longer_fr_scc) - .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) - { + if let Some(error_element) = non_placeholder_regions_in.next() { // Stop after the first error, it gets too noisy otherwise, and does not provide more information. - errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { longer_fr, error_element, placeholder, From 00a5015750434a95f491027f13f853b5c74a8cf9 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Tue, 17 Jun 2025 16:24:31 +0200 Subject: [PATCH 02/17] Revise the tests for `coroutine/auto-trait-regions` --- ...er-ranked-auto-trait-13.assumptions.stderr | 5 ++-- ...ranked-auto-trait-13.no_assumptions.stderr | 5 ++-- tests/ui/coroutine/auto-trait-regions.rs | 24 ++++++++++--------- tests/ui/coroutine/auto-trait-regions.stderr | 16 ++++++------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr index f69218740dcca..627049a2e2fa1 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr @@ -4,7 +4,7 @@ error: implementation of `Getter` is not general enough LL | assert_send(my_send_async_method(struct_with_lifetime, data)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... + = note: `Getter<'_>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'1>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` error: implementation of `Getter` is not general enough @@ -15,7 +15,6 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data)); | = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: implementation of `Getter` is not general enough --> $DIR/higher-ranked-auto-trait-13.rs:65:5 @@ -23,7 +22,7 @@ error: implementation of `Getter` is not general enough LL | assert_send(my_send_async_method(struct_with_lifetime, data)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... + = note: `Getter<'_>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'1>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr index cfbdaa8ad4beb..c6c4c1c932c37 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr @@ -4,7 +4,7 @@ error: implementation of `Getter` is not general enough LL | assert_send(my_send_async_method(struct_with_lifetime, data)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... + = note: `Getter<'_>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'1>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` error: implementation of `Getter` is not general enough @@ -15,7 +15,6 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data)); | = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: implementation of `Callable` is not general enough --> $DIR/higher-ranked-auto-trait-13.rs:65:5 @@ -32,7 +31,7 @@ error: implementation of `Getter` is not general enough LL | assert_send(my_send_async_method(struct_with_lifetime, data)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... + = note: `Getter<'_>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'1>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/coroutine/auto-trait-regions.rs b/tests/ui/coroutine/auto-trait-regions.rs index 736555b31bbd1..fae9a3788502c 100644 --- a/tests/ui/coroutine/auto-trait-regions.rs +++ b/tests/ui/coroutine/auto-trait-regions.rs @@ -20,6 +20,19 @@ impl<'a> Foo for &'a OnlyFooIfRef {} fn assert_foo(f: T) {} +fn other_assertion() { + // Disallow impls which relates lifetimes in the coroutine interior + let generator = #[coroutine] move || { + let a = A(&mut true, &mut true, No); + //~^ ERROR borrow may still be in use when coroutine yields + //~| ERROR borrow may still be in use when coroutine yields + yield; + assert_foo(a); + }; + assert_foo(generator); + //~^ ERROR not general enough +} + fn main() { // Make sure 'static is erased for coroutine interiors so we can't match it in trait selection let x: &'static _ = &OnlyFooIfStaticRef(No); @@ -39,15 +52,4 @@ fn main() { assert_foo(x); }; assert_foo(generator); // ok - - // Disallow impls which relates lifetimes in the coroutine interior - let generator = #[coroutine] move || { - let a = A(&mut true, &mut true, No); - //~^ ERROR borrow may still be in use when coroutine yields - //~| ERROR borrow may still be in use when coroutine yields - yield; - assert_foo(a); - }; - assert_foo(generator); - //~^ ERROR not general enough } diff --git a/tests/ui/coroutine/auto-trait-regions.stderr b/tests/ui/coroutine/auto-trait-regions.stderr index beb689d868d46..1976163ed56b6 100644 --- a/tests/ui/coroutine/auto-trait-regions.stderr +++ b/tests/ui/coroutine/auto-trait-regions.stderr @@ -1,5 +1,5 @@ error[E0626]: borrow may still be in use when coroutine yields - --> $DIR/auto-trait-regions.rs:45:19 + --> $DIR/auto-trait-regions.rs:26:19 | LL | let generator = #[coroutine] move || { | ------- within this coroutine @@ -15,7 +15,7 @@ LL | let generator = #[coroutine] static move || { | ++++++ error[E0626]: borrow may still be in use when coroutine yields - --> $DIR/auto-trait-regions.rs:45:30 + --> $DIR/auto-trait-regions.rs:26:30 | LL | let generator = #[coroutine] move || { | ------- within this coroutine @@ -31,22 +31,22 @@ LL | let generator = #[coroutine] static move || { | ++++++ error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:31:5 + --> $DIR/auto-trait-regions.rs:32:5 | LL | assert_foo(generator); | ^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | - = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`... - = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef` + = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2` error: implementation of `Foo` is not general enough - --> $DIR/auto-trait-regions.rs:51:5 + --> $DIR/auto-trait-regions.rs:44:5 | LL | assert_foo(generator); | ^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | - = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`... - = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2` + = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`... + = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef` error: aborting due to 4 previous errors From 93cddc9c5613613e222eca0a88d7366f6334eebf Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 27 Aug 2025 12:47:36 +0200 Subject: [PATCH 03/17] Avoid adding edges to static where we already flag other errors --- .../rustc_borrowck/src/handle_placeholders.rs | 66 +++++++++---------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 1ba1d5ca3e384..cbff534a93ee6 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -20,7 +20,7 @@ use crate::ty::VarianceDiagInfo; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; -use crate::{BorrowckInferCtxt, NllRegionVariableOrigin}; +use crate::{BorrowckInferCtxt, NllRegionVariableOrigin, bug}; /// A set of outlives constraints after rewriting to remove /// higher-kinded constraints. @@ -178,12 +178,12 @@ impl RegionTracker { }) } - /// Check for the second and final type of placeholder leak, - /// where a placeholder `'p` outlives (transitively) an existential `'e` - /// and `'e` cannot name `'p`. This is sort of a dual of `unnameable_placeholder`; - /// one of the members of this SCC cannot be named by the SCC. + /// Check for placeholder leaks where a placeholder `'p` outlives (transitively) + /// an existential `'e` and `'e` cannot name `'p`. This is sort of a dual of + /// `unnameable_placeholder`; one of the members of this SCC cannot be named by + /// the SCC itself. /// - /// Returns *a* culprit (though there may be more than one). + /// Returns *a* culprit (there may be more than one). fn reaches_existential_that_cannot_name_us(&self) -> Option { let Representative::Placeholder(_p) = self.representative else { return None; @@ -424,39 +424,33 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( // That constraint is annotated with some placeholder `unnameable` where // `unnameable` is unnameable from `r` and there is a path in the constraint graph // between them. - // - // There is one exception; if some other region in this SCC can't name `'r`, then - // we pick the region with the smallest universe in the SCC, so that a path can - // always start in `'r` to find a motivation that isn't cyclic. - let blame_to = if annotation.representative.rvid() == max_u_rvid { - // Assertion: the region that lowered our universe is an existential one and we are a placeholder! - + if annotation.representative.rvid() != max_u_rvid { + // FIXME: if we can extract a useful blame span here, future error + // reporting and constraint search can be simplified. + + added_constraints = true; + outlives_constraints.push(OutlivesConstraint { + sup: annotation.representative.rvid(), + sub: fr_static, + category: ConstraintCategory::OutlivesUnnameablePlaceholder(max_u_rvid), + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }); + } else if !(annotation.reaches_existential_that_cannot_name_us().is_some() + || annotation.reaches_other_placeholder(annotation.representative.rvid()).is_some()) + { // The SCC's representative is not nameable from some region - // that ends up in the SCC. - let small_universed_rvid = annotation.max_nameable_universe.1; - debug!( - "{small_universed_rvid:?} lowered our universe to {:?}", - annotation.max_nameable_universe() + // that ends up in the SCC. This means there is nothing for us to do. + // However, this is only possible under circumstances that produce + // errors, so we make sure that we catch them here. Otherwise, + // there might actually be soundness issues! + bug!( + "Universe of SCC {scc:?} should have been lowered by an existential or at least another placeholder but was lowered by {:?}, which is neither.", + annotation.max_nameable_universe ); - small_universed_rvid - } else { - // `max_u_rvid` is not nameable by the SCC's representative. - max_u_rvid }; - - // FIXME: if we can extract a useful blame span here, future error - // reporting and constraint search can be simplified. - - added_constraints = true; - outlives_constraints.push(OutlivesConstraint { - sup: annotation.representative.rvid(), - sub: fr_static, - category: ConstraintCategory::OutlivesUnnameablePlaceholder(blame_to), - locations: Locations::All(rustc_span::DUMMY_SP), - span: rustc_span::DUMMY_SP, - variance_info: VarianceDiagInfo::None, - from_closure: false, - }); } added_constraints } From 8f9c89c21eb379eae855aab58e3aaafb489429cf Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 25 Sep 2025 15:38:25 +0200 Subject: [PATCH 04/17] Only flag the error for placeholder-existential failures if there is actually an existential. --- .../rustc_borrowck/src/handle_placeholders.rs | 70 +++++++++++-------- compiler/rustc_borrowck/src/nll.rs | 3 + .../rustc_borrowck/src/region_infer/mod.rs | 4 +- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index cbff534a93ee6..3c2a2fea81333 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -20,7 +20,7 @@ use crate::ty::VarianceDiagInfo; use crate::type_check::free_region_relations::UniversalRegionRelations; use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; -use crate::{BorrowckInferCtxt, NllRegionVariableOrigin, bug}; +use crate::{BorrowckInferCtxt, NllRegionVariableOrigin}; /// A set of outlives constraints after rewriting to remove /// higher-kinded constraints. @@ -122,10 +122,12 @@ impl PlaceholderReachability { pub(crate) struct RegionTracker { reachable_placeholders: PlaceholderReachability, - /// The largest universe nameable from this SCC. - /// It is the smallest nameable universes of all - /// existential regions reachable from it. Small Rvids are preferred. - max_nameable_universe: (UniverseIndex, RegionVid), + /// The smallest max nameable universe of all + /// regions reachable from this SCC. + min_max_nameable_universe: UniverseIndex, + + /// The existential region with the smallest universe, if any. + min_universe_existential: Option<(UniverseIndex, RegionVid)>, /// The representative Region Variable Id for this SCC. pub(crate) representative: Representative, @@ -146,7 +148,15 @@ impl RegionTracker { Self { reachable_placeholders, - max_nameable_universe: (definition.universe, rvid), + min_universe_existential: if matches!( + definition.origin, + NllRegionVariableOrigin::Existential { .. } + ) { + Some((definition.universe, rvid)) + } else { + None + }, + min_max_nameable_universe: definition.universe, representative: Representative::new(rvid, definition), } } @@ -155,7 +165,8 @@ impl RegionTracker { /// largest nameable universe of any reachable region, or /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)` pub(crate) fn max_nameable_universe(self) -> UniverseIndex { - self.max_nameable_universe.0 + // Note that this is stricter than it might need to be! + self.min_max_nameable_universe } pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { @@ -168,13 +179,13 @@ impl RegionTracker { /// Determine if we can name all the placeholders in `other`. pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { - other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0) + other.reachable_placeholders.can_be_named_by(self.min_max_nameable_universe) } /// If this SCC reaches a placeholder it can't name, return it. fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> { self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| { - !self.max_nameable_universe().can_name(placeholder_universe) + !self.min_max_nameable_universe.can_name(placeholder_universe) }) } @@ -189,7 +200,12 @@ impl RegionTracker { return None; }; - let (reachable_lowest_max_u, reachable_lowest_max_u_rvid) = self.max_nameable_universe; + let Some((reachable_lowest_max_u, reachable_lowest_max_u_rvid)) = + self.min_universe_existential + else { + debug!("SCC universe wasn't lowered by an existential; skipping."); + return None; + }; (!self.reachable_placeholders.can_be_named_by(reachable_lowest_max_u)) .then_some(reachable_lowest_max_u_rvid) @@ -222,14 +238,20 @@ impl scc::Annotation for RegionTracker { Self { representative: self.representative.min(other.representative), - max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe), - reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), + ..self.merge_reached(other) } } + #[inline(always)] fn merge_reached(self, other: Self) -> Self { Self { - max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe), + min_universe_existential: self + .min_universe_existential + .xor(other.min_universe_existential) + .or_else(|| self.min_universe_existential.min(other.min_universe_existential)), + min_max_nameable_universe: self + .min_max_nameable_universe + .min(other.min_max_nameable_universe), reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), representative: self.representative, } @@ -438,19 +460,7 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( variance_info: VarianceDiagInfo::None, from_closure: false, }); - } else if !(annotation.reaches_existential_that_cannot_name_us().is_some() - || annotation.reaches_other_placeholder(annotation.representative.rvid()).is_some()) - { - // The SCC's representative is not nameable from some region - // that ends up in the SCC. This means there is nothing for us to do. - // However, this is only possible under circumstances that produce - // errors, so we make sure that we catch them here. Otherwise, - // there might actually be soundness issues! - bug!( - "Universe of SCC {scc:?} should have been lowered by an existential or at least another placeholder but was lowered by {:?}, which is neither.", - annotation.max_nameable_universe - ); - }; + } } added_constraints } @@ -482,12 +492,12 @@ fn find_placeholder_mismatch_errors<'tcx>( let scc = sccs.scc(rvid); let annotation = annotations.scc_to_annotation[scc]; - if let Some(existential_that_cannot_name_rvid) = - annotation.reaches_existential_that_cannot_name_us() - { + if let Some(cannot_name_rvid) = annotation.reaches_existential_that_cannot_name_us() { + debug!("Existential {cannot_name_rvid:?} lowered our universe..."); + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt { longer_fr: rvid, - existential_that_cannot_name_longer: existential_that_cannot_name_rvid, + existential_that_cannot_name_longer: cannot_name_rvid, placeholder: origin_a, }) } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index ce5be74f8a59e..a07d819495d2a 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -136,6 +136,8 @@ pub(crate) fn compute_regions<'tcx>( &mut placeholder_errors, ); + debug!("Placeholder errors: {placeholder_errors:?}"); + // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( &mut polonius_facts, @@ -188,6 +190,7 @@ pub(crate) fn compute_regions<'tcx>( let nll_errors = if region_inference_errors.is_empty() { // Only flag the higher-kinded bounds errors if there are no borrowck errors. + debug!("No region inference errors, using placeholder errors: {placeholder_errors:?}"); placeholder_errors } else { debug!("Errors already reported, skipping these: {placeholder_errors:?}"); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 97edf212f79a9..5456a0ea45a10 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1576,7 +1576,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_region_origin: NllRegionVariableOrigin, to_region: RegionVid, ) -> (BlameConstraint<'tcx>, Vec>) { - assert!(from_region != to_region, "Trying to blame a region for itself!"); + if from_region == to_region { + bug!("Trying to blame {from_region:?} for itself!"); + } let path = self.constraint_path_between_regions(from_region, to_region).unwrap(); From c90b964b057a3ce9520125be3dfce8ca22f09d0a Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 18 Sep 2025 11:39:33 +0200 Subject: [PATCH 05/17] Clarify when and why we can skip adding the : 'static constraints --- .../rustc_borrowck/src/handle_placeholders.rs | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 3c2a2fea81333..300e9fea4ee9f 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -433,34 +433,44 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( let annotation = annotations[scc]; - let Some((max_u, max_u_rvid)) = annotation.unnameable_placeholder() else { + let Some((unnameable_u, unnameable_placeholder)) = annotation.unnameable_placeholder() + else { continue; }; debug!( - "Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}", + "Placeholder {unnameable_placeholder:?} with universe {unnameable_u:?} unnameable from {scc:?} represented by {:?}", annotation.representative ); - // We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative. - // That constraint is annotated with some placeholder `unnameable` where - // `unnameable` is unnameable from `r` and there is a path in the constraint graph - // between them. - if annotation.representative.rvid() != max_u_rvid { - // FIXME: if we can extract a useful blame span here, future error - // reporting and constraint search can be simplified. - - added_constraints = true; - outlives_constraints.push(OutlivesConstraint { - sup: annotation.representative.rvid(), - sub: fr_static, - category: ConstraintCategory::OutlivesUnnameablePlaceholder(max_u_rvid), - locations: Locations::All(rustc_span::DUMMY_SP), - span: rustc_span::DUMMY_SP, - variance_info: VarianceDiagInfo::None, - from_closure: false, - }); + let representative_rvid = annotation.representative.rvid(); + + // If we got here, our representative is a placeholder and it reaches some + // region that can't name it. That's a separate error! + if representative_rvid == unnameable_placeholder { + debug!( + "No need to add constraints for a placeholder reaching an existential that can't name it; that's a separate error." + ); + assert!( + matches!(annotation.representative, Representative::Placeholder(_)), + "Representative wasn't a placeholder, which should not be possible!" + ); + continue; } + + // FIXME: if we can extract a useful blame span here, future error + // reporting and constraint search can be simplified. + + added_constraints = true; + outlives_constraints.push(OutlivesConstraint { + sup: representative_rvid, + sub: fr_static, + category: ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable_placeholder), + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }); } added_constraints } From 5ebced9f52b083c02282ae5c4c2e9695c9a24f8a Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Wed, 24 Sep 2025 22:51:32 +0200 Subject: [PATCH 06/17] Be more restrictive of when we add placeholder-existential errors --- .../rustc_borrowck/src/handle_placeholders.rs | 67 ++++++++++++------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 300e9fea4ee9f..52e8217aebc91 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -126,7 +126,13 @@ pub(crate) struct RegionTracker { /// regions reachable from this SCC. min_max_nameable_universe: UniverseIndex, - /// The existential region with the smallest universe, if any. + /// The largest universe of a placeholder in this SCC. Iff + /// an existential can name this universe it's allowed to + /// reach us. + scc_placeholder_largest_universe: Option, + + /// The reached existential region with the smallest universe, if any. This + /// is an upper bound on the universe. min_universe_existential: Option<(UniverseIndex, RegionVid)>, /// The representative Region Variable Id for this SCC. @@ -135,29 +141,39 @@ pub(crate) struct RegionTracker { impl RegionTracker { pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { - let reachable_placeholders = - if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) { - PlaceholderReachability::Placeholders { - max_universe: (definition.universe, rvid), + use NllRegionVariableOrigin::*; + use PlaceholderReachability::*; + + let min_max_nameable_universe = definition.universe; + let representative = Representative::new(rvid, definition); + let universe_and_rvid = (definition.universe, rvid); + + match definition.origin { + FreeRegion => Self { + reachable_placeholders: NoPlaceholders, + min_max_nameable_universe, + scc_placeholder_largest_universe: None, + min_universe_existential: None, + representative, + }, + Placeholder(_) => Self { + reachable_placeholders: Placeholders { + max_universe: universe_and_rvid, min_placeholder: rvid, max_placeholder: rvid, - } - } else { - PlaceholderReachability::NoPlaceholders - }; - - Self { - reachable_placeholders, - min_universe_existential: if matches!( - definition.origin, - NllRegionVariableOrigin::Existential { .. } - ) { - Some((definition.universe, rvid)) - } else { - None + }, + min_max_nameable_universe, + scc_placeholder_largest_universe: Some(definition.universe), + min_universe_existential: None, + representative, + }, + Existential { .. } => Self { + reachable_placeholders: NoPlaceholders, + min_max_nameable_universe, + scc_placeholder_largest_universe: None, + min_universe_existential: Some(universe_and_rvid), + representative, }, - min_max_nameable_universe: definition.universe, - representative: Representative::new(rvid, definition), } } @@ -196,7 +212,7 @@ impl RegionTracker { /// /// Returns *a* culprit (there may be more than one). fn reaches_existential_that_cannot_name_us(&self) -> Option { - let Representative::Placeholder(_p) = self.representative else { + let Some(required_universe) = self.scc_placeholder_largest_universe else { return None; }; @@ -207,8 +223,7 @@ impl RegionTracker { return None; }; - (!self.reachable_placeholders.can_be_named_by(reachable_lowest_max_u)) - .then_some(reachable_lowest_max_u_rvid) + (!reachable_lowest_max_u.can_name(required_universe)).then_some(reachable_lowest_max_u_rvid) } /// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`, @@ -238,6 +253,9 @@ impl scc::Annotation for RegionTracker { Self { representative: self.representative.min(other.representative), + scc_placeholder_largest_universe: self + .scc_placeholder_largest_universe + .max(other.scc_placeholder_largest_universe), ..self.merge_reached(other) } } @@ -254,6 +272,7 @@ impl scc::Annotation for RegionTracker { .min(other.min_max_nameable_universe), reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), representative: self.representative, + scc_placeholder_largest_universe: self.scc_placeholder_largest_universe, } } } From fe038611b52926d4a91eaeb6782892f2000fe913 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 9 Oct 2025 15:29:17 +0200 Subject: [PATCH 07/17] Remove precise placeholder tracking from region inference --- .../src/diagnostics/region_errors.rs | 71 +++---- .../rustc_borrowck/src/handle_placeholders.rs | 22 ++- .../rustc_borrowck/src/region_infer/mod.rs | 179 ++++++------------ .../src/region_infer/opaque_types/mod.rs | 16 +- .../region_infer/opaque_types/region_ctxt.rs | 6 +- .../rustc_borrowck/src/region_infer/values.rs | 111 +---------- compiler/rustc_borrowck/src/type_check/mod.rs | 42 ++-- 7 files changed, 124 insertions(+), 323 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 982a26ef800e6..2938d1b49bc66 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -224,64 +224,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { &self, diag: &mut Diag<'_>, lower_bound: RegionVid, - ) { + ) -> Option<()> { let tcx = self.infcx.tcx; // find generic associated types in the given region 'lower_bound' - let gat_id_and_generics = self - .regioncx - .placeholders_contained_in(lower_bound) - .map(|placeholder| { - if let Some(id) = placeholder.bound.kind.get_id() - && let Some(placeholder_id) = id.as_local() - && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id) - && let Some(generics_impl) = - tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics() - { - Some((gat_hir_id, generics_impl)) - } else { - None - } - }) - .collect::>(); - debug!(?gat_id_and_generics); + let scc = self.regioncx.constraint_sccs().scc(lower_bound); + let placeholder: ty::PlaceholderRegion = self.regioncx.placeholder_representative(scc)?; + let placeholder_id = placeholder.bound.kind.get_id()?.as_local()?; + let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id); + let generics_impl = + self.infcx.tcx.parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id)).generics()?; // Look for the where-bound which introduces the placeholder. // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>` // and `T: for<'a> Trait`<'a>. let mut hrtb_bounds = vec![]; - gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| { - for pred in generics.predicates { - let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = - pred.kind - else { - continue; - }; - if bound_generic_params - .iter() - .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) - .is_some() - { - for bound in *bounds { - hrtb_bounds.push(bound); - } - } else { - for bound in *bounds { - if let Trait(trait_bound) = bound { - if trait_bound - .bound_generic_params - .iter() - .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) - .is_some() - { - hrtb_bounds.push(bound); - return; - } - } - } + + for pred in generics_impl.predicates { + let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = + pred.kind + else { + continue; + }; + if bound_generic_params + .iter() + .rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id) + .is_some() + { + for bound in *bounds { + hrtb_bounds.push(bound); } } - }); + } debug!(?hrtb_bounds); let mut suggestions = vec![]; @@ -327,6 +301,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Applicability::MaybeIncorrect, ); } + Some(()) } /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`. diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 52e8217aebc91..4fd8ca886b61b 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -14,7 +14,7 @@ use tracing::{debug, instrument, trace}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet}; use crate::consumers::OutlivesConstraint; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::region_infer::values::{LivenessValues, PlaceholderIndices}; +use crate::region_infer::values::LivenessValues; use crate::region_infer::{ConstraintSccs, RegionDefinition, Representative, TypeTest}; use crate::ty::VarianceDiagInfo; use crate::type_check::free_region_relations::UniversalRegionRelations; @@ -32,7 +32,6 @@ pub(crate) struct LoweredConstraints<'tcx> { pub(crate) type_tests: Vec>, pub(crate) liveness_constraints: LivenessValues, pub(crate) universe_causes: FxIndexMap>, - pub(crate) placeholder_indices: PlaceholderIndices, } impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { @@ -62,7 +61,7 @@ impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { } #[derive(Copy, Debug, Clone, PartialEq, Eq)] -enum PlaceholderReachability { +pub(crate) enum PlaceholderReachability { /// This SCC reaches no placeholders. NoPlaceholders, /// This SCC reaches at least one placeholder. @@ -120,7 +119,7 @@ impl PlaceholderReachability { /// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] pub(crate) struct RegionTracker { - reachable_placeholders: PlaceholderReachability, + pub(crate) reachable_placeholders: PlaceholderReachability, /// The smallest max nameable universe of all /// regions reachable from this SCC. @@ -245,6 +244,16 @@ impl RegionTracker { PlaceholderReachability::Placeholders { min_placeholder, .. } => Some(min_placeholder), } } + + /// If this SCC reaches at least one placeholder, return + /// its region vid. If there's more than one, return the one + /// with the smallest vid. + pub(crate) fn reached_placeholder(&self) -> Option { + match self.reachable_placeholders { + PlaceholderReachability::NoPlaceholders => None, + PlaceholderReachability::Placeholders { min_placeholder, .. } => Some(min_placeholder), + } + } } impl scc::Annotation for RegionTracker { @@ -350,12 +359,11 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( let (definitions, has_placeholders) = region_definitions(infcx, universal_regions); let MirTypeckRegionConstraints { - placeholder_indices, - placeholder_index_to_region: _, liveness_constraints, mut outlives_constraints, universe_causes, type_tests, + placeholder_to_region: _ } = constraints; let fr_static = universal_regions.fr_static; @@ -385,7 +393,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( outlives_constraints: Frozen::freeze(outlives_constraints), liveness_constraints, universe_causes, - placeholder_indices, }; } debug!("Placeholders present; activating placeholder handling logic!"); @@ -426,7 +433,6 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( type_tests, liveness_constraints, universe_causes, - placeholder_indices, } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5456a0ea45a10..cf5b5ae52b15b 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -26,7 +26,7 @@ use crate::constraints::graph::NormalConstraintGraph; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; +use crate::handle_placeholders::{LoweredConstraints, PlaceholderReachability, RegionTracker}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; @@ -303,14 +303,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { outlives_constraints, scc_annotations, type_tests, - liveness_constraints, + mut liveness_constraints, universe_causes, - placeholder_indices, } = lowered_constraints; debug!("universal_regions: {:#?}", universal_region_relations.universal_regions); debug!("outlives constraints: {:#?}", outlives_constraints); - debug!("placeholder_indices: {:#?}", placeholder_indices); debug!("type tests: {:#?}", type_tests); let constraint_graph = Frozen::freeze(outlives_constraints.graph(definitions.len())); @@ -319,15 +317,29 @@ impl<'tcx> RegionInferenceContext<'tcx> { sccs_info(infcx, &constraint_sccs); } - let mut scc_values = - RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + let mut scc_values = RegionValues::new(location_map, universal_regions.len()); for region in liveness_constraints.regions() { let scc = constraint_sccs.scc(region); scc_values.merge_liveness(scc, region, &liveness_constraints); } - let mut result = Self { + for variable in definitions.indices() { + if let NllRegionVariableOrigin::FreeRegion = definitions[variable].origin { + // For each free, universally quantified region X: + + let scc = constraint_sccs.scc(variable); + + // Add all nodes in the CFG to liveness constraints + liveness_constraints.add_all_points(variable); + scc_values.add_all_points(scc); + + // Add `end(X)` into the set for X. + scc_values.add_element(scc, variable); + } + } + + Self { definitions, liveness_constraints, constraints: outlives_constraints, @@ -338,90 +350,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_values, type_tests, universal_region_relations, - }; - - result.init_free_and_bound_regions(); - - result - } - - /// Initializes the region variables for each universally - /// quantified region (lifetime parameter). The first N variables - /// always correspond to the regions appearing in the function - /// signature (both named and anonymous) and where-clauses. This - /// function iterates over those regions and initializes them with - /// minimum values. - /// - /// For example: - /// ```ignore (illustrative) - /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } - /// ``` - /// would initialize two variables like so: - /// ```ignore (illustrative) - /// R0 = { CFG, R0 } // 'a - /// R1 = { CFG, R0, R1 } // 'b - /// ``` - /// Here, R0 represents `'a`, and it contains (a) the entire CFG - /// and (b) any universally quantified regions that it outlives, - /// which in this case is just itself. R1 (`'b`) in contrast also - /// outlives `'a` and hence contains R0 and R1. - /// - /// This bit of logic also handles invalid universe relations - /// for higher-kinded types. - /// - /// We Walk each SCC `A` and `B` such that `A: B` - /// and ensure that universe(A) can see universe(B). - /// - /// This serves to enforce the 'empty/placeholder' hierarchy - /// (described in more detail on `RegionKind`): - /// - /// ```ignore (illustrative) - /// static -----+ - /// | | - /// empty(U0) placeholder(U1) - /// | / - /// empty(U1) - /// ``` - /// - /// In particular, imagine we have variables R0 in U0 and R1 - /// created in U1, and constraints like this; - /// - /// ```ignore (illustrative) - /// R1: !1 // R1 outlives the placeholder in U1 - /// R1: R0 // R1 outlives R0 - /// ``` - /// - /// Here, we wish for R1 to be `'static`, because it - /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way. - /// - /// Thanks to this loop, what happens is that the `R1: R0` - /// constraint has lowered the universe of `R1` to `U0`, which in turn - /// means that the `R1: !1` constraint here will cause - /// `R1` to become `'static`. - fn init_free_and_bound_regions(&mut self) { - for variable in self.definitions.indices() { - let scc = self.constraint_sccs.scc(variable); - - match self.definitions[variable].origin { - NllRegionVariableOrigin::FreeRegion => { - // For each free, universally quantified region X: - - // Add all nodes in the CFG to liveness constraints - self.liveness_constraints.add_all_points(variable); - self.scc_values.add_all_points(scc); - - // Add `end(X)` into the set for X. - self.scc_values.add_element(scc, variable); - } - - NllRegionVariableOrigin::Placeholder(placeholder) => { - self.scc_values.add_element(scc, placeholder); - } - - NllRegionVariableOrigin::Existential { .. } => { - // For existential, regions, nothing to do. - } - } } } @@ -478,14 +406,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_values.region_value_str(scc) } - pub(crate) fn placeholders_contained_in( - &self, - r: RegionVid, - ) -> impl Iterator { - let scc = self.constraint_sccs.scc(r); - self.scc_values.placeholders_contained_in(scc) - } - /// Performs region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -691,8 +611,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // It doesn't matter *what* universe because the promoted `T` will // always be in the root universe. - if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() { - debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p); + if let Some(min_placeholder) = self.scc_annotations[r_scc].reached_placeholder() { + debug!( + "encountered placeholder in higher universe: {min_placeholder:?}, requiring 'static" + ); let static_r = self.universal_regions().fr_static; propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject, @@ -1319,23 +1241,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - // FIXME(amandasystems): This is an inlined version of elements_contained_in, without - // placeholders, which are handled separately. Later, when placeholders are removed - // from scc_values, this part will just be elements_contained_in(): - let mut non_placeholder_regions_in = self - .scc_values - .locations_outlived_by(longer_fr_scc) - .map(RegionElement::Location) - .chain( - self.scc_values - .universal_regions_outlived_by(longer_fr_scc) - .map(RegionElement::RootUniversalRegion), - ); - // If we have some bound universal region `'a`, then the only // elements it can contain is itself -- we don't know anything // else about it! - if let Some(error_element) = non_placeholder_regions_in.next() { + if let Some(error_element) = self.scc_values.elements_contained_in(longer_fr_scc).next() { // Stop after the first error, it gets too noisy otherwise, and does not provide more information. errors_buffer.push(RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { longer_fr, @@ -1537,14 +1446,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { match *element { RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), RegionElement::RootUniversalRegion(r) => r, - RegionElement::PlaceholderRegion(error_placeholder) => self - .definitions - .iter_enumerated() - .find_map(|(r, definition)| match definition.origin { - NllRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), - _ => None, - }) - .unwrap(), } } @@ -1553,6 +1454,38 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self.definitions[r] } + /// If the representative of an SCC is a placeholder, return + /// its originating `PlaceholderRegion`. + pub(crate) fn placeholder_representative( + &self, + scc: ConstraintSccIndex, + ) -> Option { + if let Representative::Placeholder(r) = self.scc_annotations[scc].representative + && let NllRegionVariableOrigin::Placeholder(p) = self.definitions[r].origin + { + Some(p) + } else { + None + } + } + + /// If this SCC contains precisely one placeholder, return it. + pub(crate) fn reaches_single_placeholder( + &self, + scc: ConstraintSccIndex, + ) -> Option { + if let PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. } = + self.scc_annotations[scc].reachable_placeholders + && min_placeholder == max_placeholder + && let NllRegionVariableOrigin::Placeholder(p) = + self.definitions[min_placeholder].origin + { + Some(p) + } else { + None + } + } + /// Check if the SCC of `r` contains `upper`. pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { let r_scc = self.constraint_sccs.scc(r); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 0c4a82f3d2f36..f48846ef6a6fe 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -618,15 +618,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Special handling of higher-ranked regions. if !self.max_nameable_universe(scc).is_root() { - match self.scc_values.placeholders_contained_in(scc).enumerate().last() { + // FIXME: consider if `self.placeholder_representative(scc)` is enough here. + // That will simplify the logic quite a bit, but won't work if the placeholder + // isn't *in* `scc` but rather was *reached from* `scc`, presumably because + // `scc` is some transient boring region that's essentially a placeholder + // under a different name. + return match self.reaches_single_placeholder(scc) { // If the region contains a single placeholder then they're equal. - Some((0, placeholder)) => { - return ty::Region::new_placeholder(tcx, placeholder); - } - + Some(placeholder) => ty::Region::new_placeholder(tcx, placeholder), // Fallback: this will produce a cryptic error message. - _ => return region, - } + _ => region, + }; } // Find something that we can name diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs index 90b15cbdd2cc8..1265adfad903d 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -74,10 +74,8 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { let scc_annotations = scc_annotations.scc_to_annotation; // Unlike the `RegionInferenceContext`, we only care about free regions - // and fully ignore liveness and placeholders. - let placeholder_indices = Default::default(); - let mut scc_values = - RegionValues::new(location_map, universal_regions.len(), placeholder_indices); + // and fully ignore liveness. + let mut scc_values = RegionValues::new(location_map, universal_regions.len()); for variable in definitions.indices() { let scc = constraint_sccs.scc(variable); match definitions[variable].origin { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index eb611fa34757f..8fd58a81c35ea 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -1,24 +1,18 @@ use std::fmt::Debug; use std::rc::Rc; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_index::Idx; use rustc_index::bit_set::SparseBitMatrix; use rustc_index::interval::{IntervalSet, SparseIntervalMatrix}; use rustc_middle::mir::{BasicBlock, Location}; -use rustc_middle::ty::{self, RegionVid}; +use rustc_middle::ty::RegionVid; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use tracing::debug; use crate::BorrowIndex; use crate::polonius::LiveLoans; -rustc_index::newtype_index! { - /// A single integer representing a `ty::Placeholder`. - #[debug_format = "PlaceholderIndex({})"] - pub(crate) struct PlaceholderIndex {} -} - /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. #[derive(Debug, Clone, PartialEq)] @@ -29,10 +23,6 @@ pub(crate) enum RegionElement { /// A universally quantified region from the root universe (e.g., /// a lifetime parameter). RootUniversalRegion(RegionVid), - - /// A placeholder (e.g., instantiated from a `for<'a> fn(&'a u32)` - /// type). - PlaceholderRegion(ty::PlaceholderRegion), } /// Records the CFG locations where each region is live. When we initially compute liveness, we use @@ -191,38 +181,6 @@ impl LivenessValues { } } -/// Maps from `ty::PlaceholderRegion` values that are used in the rest of -/// rustc to the internal `PlaceholderIndex` values that are used in -/// NLL. -#[derive(Debug, Default)] -#[derive(Clone)] // FIXME(#146079) -pub(crate) struct PlaceholderIndices { - indices: FxIndexSet, -} - -impl PlaceholderIndices { - /// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion` - pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { - let (index, _) = self.indices.insert_full(placeholder); - index.into() - } - - pub(crate) fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { - self.indices.get_index_of(&placeholder).unwrap().into() - } - - pub(crate) fn lookup_placeholder( - &self, - placeholder: PlaceholderIndex, - ) -> ty::PlaceholderRegion { - self.indices[placeholder.index()] - } - - pub(crate) fn len(&self) -> usize { - self.indices.len() - } -} - /// Stores the full values for a set of regions (in contrast to /// `LivenessValues`, which only stores those points in the where a /// region is live). The full value for a region may contain points in @@ -243,32 +201,20 @@ impl PlaceholderIndices { /// it would also contain various points from within the function. pub(crate) struct RegionValues { location_map: Rc, - placeholder_indices: PlaceholderIndices, points: SparseIntervalMatrix, free_regions: SparseBitMatrix, - - /// Placeholders represent bound regions -- so something like `'a` - /// in `for<'a> fn(&'a u32)`. - placeholders: SparseBitMatrix, } impl RegionValues { /// Creates a new set of "region values" that tracks causal information. /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. - pub(crate) fn new( - location_map: Rc, - num_universal_regions: usize, - placeholder_indices: PlaceholderIndices, - ) -> Self { + pub(crate) fn new(location_map: Rc, num_universal_regions: usize) -> Self { let num_points = location_map.num_points(); - let num_placeholders = placeholder_indices.len(); Self { location_map, points: SparseIntervalMatrix::new(num_points), - placeholder_indices, free_regions: SparseBitMatrix::new(num_universal_regions), - placeholders: SparseBitMatrix::new(num_placeholders), } } @@ -287,9 +233,7 @@ impl RegionValues { /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to: /// r_from`). pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool { - self.points.union_rows(r_from, r_to) - | self.free_regions.union_rows(r_from, r_to) - | self.placeholders.union_rows(r_from, r_to) + self.points.union_rows(r_from, r_to) | self.free_regions.union_rows(r_from, r_to) } /// Returns `true` if the region `r` contains the given element. @@ -355,29 +299,11 @@ impl RegionValues { self.free_regions.row(r).into_iter().flat_map(|set| set.iter()) } - /// Returns all the elements contained in a given region's value. - pub(crate) fn placeholders_contained_in( - &self, - r: N, - ) -> impl Iterator { - self.placeholders - .row(r) - .into_iter() - .flat_map(|set| set.iter()) - .map(move |p| self.placeholder_indices.lookup_placeholder(p)) - } - /// Returns all the elements contained in a given region's value. pub(crate) fn elements_contained_in(&self, r: N) -> impl Iterator { - let points_iter = self.locations_outlived_by(r).map(RegionElement::Location); - - let free_regions_iter = - self.universal_regions_outlived_by(r).map(RegionElement::RootUniversalRegion); - - let placeholder_universes_iter = - self.placeholders_contained_in(r).map(RegionElement::PlaceholderRegion); - - points_iter.chain(free_regions_iter).chain(placeholder_universes_iter) + self.locations_outlived_by(r) + .map(RegionElement::Location) + .chain(self.universal_regions_outlived_by(r).map(RegionElement::RootUniversalRegion)) } /// Returns a "pretty" string value of the region. Meant for debugging. @@ -414,18 +340,6 @@ impl ToElementIndex for RegionVid { } } -impl ToElementIndex for ty::PlaceholderRegion { - fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { - let index = values.placeholder_indices.lookup_index(self); - values.placeholders.insert(row, index) - } - - fn contained_in_row(self, values: &RegionValues, row: N) -> bool { - let index = values.placeholder_indices.lookup_index(self); - values.placeholders.contains(row, index) - } -} - /// For debugging purposes, returns a pretty-printed string of the given points. pub(crate) fn pretty_print_points( location_map: &DenseLocationMap, @@ -485,17 +399,6 @@ fn pretty_print_region_elements(elements: impl IntoIterator { - if let Some((location1, location2)) = open_location { - push_sep(&mut result); - push_location_range(&mut result, location1, location2); - open_location = None; - } - - push_sep(&mut result); - result.push_str(&format!("{placeholder:?}")); - } } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5f86e8646c03f..b4685d641565c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -5,13 +5,13 @@ use std::{fmt, iter, mem}; use rustc_abi::FieldIdx; use rustc_data_structures::frozen::Frozen; -use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexSlice; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; @@ -45,7 +45,7 @@ use crate::diagnostics::UniverseInfo; use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; -use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}; +use crate::region_infer::values::LivenessValues; use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::type_check::free_region_relations::{CreateResult, UniversalRegionRelations}; use crate::universal_regions::{DefiningTy, UniversalRegions}; @@ -108,12 +108,11 @@ pub(crate) fn type_check<'tcx>( location_map: Rc, ) -> MirTypeckResults<'tcx> { let mut constraints = MirTypeckRegionConstraints { - placeholder_indices: PlaceholderIndices::default(), - placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)), outlives_constraints: OutlivesConstraintSet::default(), type_tests: Vec::default(), universe_causes: FxIndexMap::default(), + placeholder_to_region: Default::default(), }; let CreateResult { @@ -253,20 +252,6 @@ pub(crate) struct MirTypeckResults<'tcx> { /// program to be considered well-typed. #[derive(Clone)] // FIXME(#146079) pub(crate) struct MirTypeckRegionConstraints<'tcx> { - /// Maps from a `ty::Placeholder` to the corresponding - /// `PlaceholderIndex` bit that we will use for it. - /// - /// To keep everything in sync, do not insert this set - /// directly. Instead, use the `placeholder_region` helper. - pub(crate) placeholder_indices: PlaceholderIndices, - - /// Each time we add a placeholder to `placeholder_indices`, we - /// also create a corresponding "representative" region vid for - /// that wraps it. This vector tracks those. This way, when we - /// convert the same `ty::RePlaceholder(p)` twice, we can map to - /// the same underlying `RegionVid`. - pub(crate) placeholder_index_to_region: IndexVec>, - /// In general, the type-checker is not responsible for enforcing /// liveness constraints; this job falls to the region inferencer, /// which performs a liveness analysis. However, in some limited @@ -281,6 +266,11 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { pub(crate) universe_causes: FxIndexMap>, pub(crate) type_tests: Vec>, + + /// For each placeholder we create a corresponding representative region vid. + /// This map tracks those. This way, when we convert the same `ty::RePlaceholder(p)` + /// twice, we can map to the same underlying `RegionVid`. + pub(crate) placeholder_to_region: FxHashMap>, } impl<'tcx> MirTypeckRegionConstraints<'tcx> { @@ -291,16 +281,10 @@ impl<'tcx> MirTypeckRegionConstraints<'tcx> { infcx: &InferCtxt<'tcx>, placeholder: ty::PlaceholderRegion, ) -> ty::Region<'tcx> { - let placeholder_index = self.placeholder_indices.insert(placeholder); - match self.placeholder_index_to_region.get(placeholder_index) { - Some(&v) => v, - None => { - let origin = NllRegionVariableOrigin::Placeholder(placeholder); - let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); - self.placeholder_index_to_region.push(region); - region - } - } + *self.placeholder_to_region.entry(placeholder).or_insert_with(|| { + let origin = NllRegionVariableOrigin::Placeholder(placeholder); + infcx.next_nll_region_var_in_universe(origin, placeholder.universe) + }) } } From 6db3b331e8e9f160d6376714871da3d3a99267ae Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 3 Nov 2025 11:25:16 +0100 Subject: [PATCH 08/17] More explicit version of placeholder constraint handling --- .../rustc_borrowck/src/handle_placeholders.rs | 133 +++++++++++++----- 1 file changed, 98 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 4fd8ca886b61b..d16b34417200c 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -115,6 +115,72 @@ impl PlaceholderReachability { } } +#[derive(Copy, Debug, Clone)] + +enum PlaceholderConstraints { + /// The SCC must be able to name this placeholder, in the SCC. + Name(UniverseIndex), + /// Any placeholder we reach must be nameable from this existential. + NameableBy(RegionVid, UniverseIndex), + /// Both constraints at the same time. + NameAndBeNamed { name: UniverseIndex, nameable_by: (RegionVid, UniverseIndex) }, +} + +impl PlaceholderConstraints { + fn add_name(self, name: UniverseIndex) -> Self { + use PlaceholderConstraints::*; + + match self { + Name(universe_index) => Name(universe_index.min(name)), + NameableBy(nb_rvid, nb_u) => NameAndBeNamed { name, nameable_by: (nb_rvid, nb_u) }, + NameAndBeNamed { name: my_name, nameable_by } => { + NameAndBeNamed { name: my_name.min(name), nameable_by } + } + } + } + + fn add_nameable_by(self, region_vid: RegionVid, universe: UniverseIndex) -> Self { + use PlaceholderConstraints::*; + + match self { + Name(universe_index) => { + NameAndBeNamed { name: universe_index, nameable_by: (region_vid, universe) } + } + NameableBy(my_rvid, my_universe_index) => { + let (u, rvid) = ((my_universe_index, my_rvid)).min((universe, region_vid)); + NameableBy(rvid, u) + } + NameAndBeNamed { name, nameable_by } => { + let (u, rvid) = ((nameable_by.1, nameable_by.0)).min((universe, region_vid)); + NameAndBeNamed { name, nameable_by: (rvid, u) } + } + } + } + + fn merge_scc(self, other: Self) -> Self { + use PlaceholderConstraints::*; + match other { + Name(universe_index) | NameAndBeNamed { name: universe_index, .. } => { + self.add_name(universe_index) + } + _ => self, + } + .merge_reached(other) + } + + #[inline(always)] + fn merge_reached(self, reached: Self) -> Self { + use PlaceholderConstraints::*; + match reached { + Name(_) => self, + NameableBy(region_vid, universe_index) + | NameAndBeNamed { nameable_by: (region_vid, universe_index), .. } => { + self.add_nameable_by(region_vid, universe_index) + } + } + } +} + /// An annotation for region graph SCCs that tracks /// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] @@ -125,14 +191,8 @@ pub(crate) struct RegionTracker { /// regions reachable from this SCC. min_max_nameable_universe: UniverseIndex, - /// The largest universe of a placeholder in this SCC. Iff - /// an existential can name this universe it's allowed to - /// reach us. - scc_placeholder_largest_universe: Option, - - /// The reached existential region with the smallest universe, if any. This - /// is an upper bound on the universe. - min_universe_existential: Option<(UniverseIndex, RegionVid)>, + /// State tracking for exceptional circumstances. + exception: Option, /// The representative Region Variable Id for this SCC. pub(crate) representative: Representative, @@ -141,6 +201,7 @@ pub(crate) struct RegionTracker { impl RegionTracker { pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { use NllRegionVariableOrigin::*; + use PlaceholderConstraints::*; use PlaceholderReachability::*; let min_max_nameable_universe = definition.universe; @@ -151,8 +212,7 @@ impl RegionTracker { FreeRegion => Self { reachable_placeholders: NoPlaceholders, min_max_nameable_universe, - scc_placeholder_largest_universe: None, - min_universe_existential: None, + exception: None, representative, }, Placeholder(_) => Self { @@ -162,15 +222,13 @@ impl RegionTracker { max_placeholder: rvid, }, min_max_nameable_universe, - scc_placeholder_largest_universe: Some(definition.universe), - min_universe_existential: None, + exception: Some(Name(definition.universe)), representative, }, Existential { .. } => Self { reachable_placeholders: NoPlaceholders, min_max_nameable_universe, - scc_placeholder_largest_universe: None, - min_universe_existential: Some(universe_and_rvid), + exception: Some(NameableBy(rvid, definition.universe)), representative, }, } @@ -211,18 +269,16 @@ impl RegionTracker { /// /// Returns *a* culprit (there may be more than one). fn reaches_existential_that_cannot_name_us(&self) -> Option { - let Some(required_universe) = self.scc_placeholder_largest_universe else { - return None; - }; - - let Some((reachable_lowest_max_u, reachable_lowest_max_u_rvid)) = - self.min_universe_existential - else { - debug!("SCC universe wasn't lowered by an existential; skipping."); - return None; - }; - - (!reachable_lowest_max_u.can_name(required_universe)).then_some(reachable_lowest_max_u_rvid) + if let Some(PlaceholderConstraints::NameAndBeNamed { + name: p_u, + nameable_by: (ex_rv, ex_u), + }) = self.exception + && ex_u.cannot_name(p_u) + { + Some(ex_rv) + } else { + None + } } /// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`, @@ -262,26 +318,33 @@ impl scc::Annotation for RegionTracker { Self { representative: self.representative.min(other.representative), - scc_placeholder_largest_universe: self - .scc_placeholder_largest_universe - .max(other.scc_placeholder_largest_universe), + exception: match (self.exception, other.exception) { + (None, other) | (other, None) => other, + (Some(ours), Some(theirs)) => Some(ours.merge_scc(theirs)), + }, ..self.merge_reached(other) } } #[inline(always)] fn merge_reached(self, other: Self) -> Self { + use PlaceholderConstraints::*; Self { - min_universe_existential: self - .min_universe_existential - .xor(other.min_universe_existential) - .or_else(|| self.min_universe_existential.min(other.min_universe_existential)), + exception: match (self.exception, other.exception) { + // Propagate only reachability (nameable by). + (None, None) => None, + (None, Some(NameableBy(r, u))) + | (None, Some(NameAndBeNamed { nameable_by: (r, u), .. })) => { + Some(NameableBy(r, u)) + } + (Some(_), None) | (None, Some(Name(_))) => self.exception, + (Some(this), Some(that)) => Some(this.merge_reached(that)), + }, min_max_nameable_universe: self .min_max_nameable_universe .min(other.min_max_nameable_universe), reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), representative: self.representative, - scc_placeholder_largest_universe: self.scc_placeholder_largest_universe, } } } @@ -363,7 +426,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( mut outlives_constraints, universe_causes, type_tests, - placeholder_to_region: _ + placeholder_to_region: _, } = constraints; let fr_static = universal_regions.fr_static; From 7e758c4e082e1fb86bff628037026ec51a55f730 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 3 Nov 2025 15:26:11 +0100 Subject: [PATCH 09/17] Simplify the placeholder error reporting, add skipping logic --- .../src/diagnostics/region_errors.rs | 72 +++---------------- .../rustc_borrowck/src/handle_placeholders.rs | 68 ++++++++---------- compiler/rustc_borrowck/src/lib.rs | 32 +++++---- .../rustc_borrowck/src/region_infer/mod.rs | 1 - .../region_infer/opaque_types/region_ctxt.rs | 4 +- 5 files changed, 60 insertions(+), 117 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 2938d1b49bc66..3aa5f8b8b608e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -104,27 +104,10 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// 'a outlives 'b, and both are placeholders. - PlaceholderOutlivesPlaceholder { - rvid_a: RegionVid, - rvid_b: RegionVid, - origin_a: ty::PlaceholderRegion, - origin_b: ty::PlaceholderRegion, - }, - - /// Indicates that a placeholder has a universe too large for one - /// of its member existentials, or, equivalently, that there is - /// a path through the outlives constraint graph from a placeholder - /// to an existential region that cannot name it. - PlaceholderOutlivesExistentialThatCannotNameIt { - /// the placeholder that transitively outlives an - /// existential that shouldn't leak into it - longer_fr: RegionVid, - /// The existential leaking into `longer_fr`. - existential_that_cannot_name_longer: RegionVid, - // `longer_fr`'s originating placeholder region. - placeholder: ty::PlaceholderRegion, - }, + /// 'a outlives 'b, which does not hold. 'a is always a + /// placeholder and 'b is either an existential that cannot name + /// 'a, or another placeholder. + PlaceholderOutlivesIllegalRegion { longer_fr: RegionVid, illegally_outlived_r: RegionVid }, /// Higher-ranked subtyping error. A placeholder outlives /// either a location or a universal region. @@ -133,8 +116,6 @@ pub(crate) enum RegionErrorKind<'tcx> { longer_fr: RegionVid, /// The region element that erroneously must be outlived by `longer_fr`. error_element: RegionElement, - /// The placeholder region. - placeholder: ty::PlaceholderRegion, }, /// Any other lifetime error. @@ -361,53 +342,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { longer_fr, - placeholder, error_element, } => self.report_erroneous_rvid_reaches_placeholder( longer_fr, - placeholder, self.regioncx.region_from_element(longer_fr, &error_element), ), - RegionErrorKind::PlaceholderOutlivesPlaceholder { - rvid_a, - rvid_b, - origin_a, - origin_b, - } => { - debug!( - "Placeholder mismatch: {rvid_a:?} ({origin_a:?}) reaches {rvid_b:?} ({origin_b:?})" - ); - - let cause = self - .regioncx - .best_blame_constraint( - rvid_a, - NllRegionVariableOrigin::Placeholder(origin_a), - rvid_b, - ) - .0 - .cause; - - // FIXME We may be able to shorten the code path here, and immediately - // report a `RegionResolutionError::UpperBoundUniverseConflict`, but - // that's left for a future refactoring. - self.regioncx.universe_info(origin_a.universe).report_erroneous_element( - self, - origin_a, - Some(origin_b), - cause, - ); - } - RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt { - longer_fr, - existential_that_cannot_name_longer, - placeholder, - } => self.report_erroneous_rvid_reaches_placeholder( + RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, - placeholder, - existential_that_cannot_name_longer, - ), + illegally_outlived_r, + } => { + self.report_erroneous_rvid_reaches_placeholder(longer_fr, illegally_outlived_r) + } RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { if is_reported { diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index d16b34417200c..0181b45ea4e16 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -35,8 +35,16 @@ pub(crate) struct LoweredConstraints<'tcx> { } impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { - pub(crate) fn init(definitions: &'d IndexVec>) -> Self { - Self { scc_to_annotation: IndexVec::new(), definitions } + pub(crate) fn with_checking_placeholders( + definitions: &'d IndexVec>, + ) -> Self { + Self { scc_to_annotation: IndexVec::new(), definitions, check_placeholders: true } + } + + pub(crate) fn without_checking_placeholders( + definitions: &'d IndexVec>, + ) -> Self { + Self { scc_to_annotation: IndexVec::new(), definitions, check_placeholders: true } } } @@ -44,11 +52,14 @@ impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { pub(crate) scc_to_annotation: IndexVec, definitions: &'d IndexVec>, + /// True if we apply extra validity checking logic to placeholder + /// regions, false to skip it. Used only for performance reasons. + check_placeholders: bool, } impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { fn new(&self, element: RegionVid) -> RegionTracker { - RegionTracker::new(element, &self.definitions[element]) + RegionTracker::new(element, &self.definitions[element], self.check_placeholders) } fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { @@ -199,7 +210,11 @@ pub(crate) struct RegionTracker { } impl RegionTracker { - pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { + pub(crate) fn new( + rvid: RegionVid, + definition: &RegionDefinition<'_>, + check_placeholders: bool, + ) -> Self { use NllRegionVariableOrigin::*; use PlaceholderConstraints::*; use PlaceholderReachability::*; @@ -222,13 +237,13 @@ impl RegionTracker { max_placeholder: rvid, }, min_max_nameable_universe, - exception: Some(Name(definition.universe)), + exception: check_placeholders.then_some(Name(definition.universe)), representative, }, Existential { .. } => Self { reachable_placeholders: NoPlaceholders, min_max_nameable_universe, - exception: Some(NameableBy(rvid, definition.universe)), + exception: check_placeholders.then_some(NameableBy(rvid, definition.universe)), representative, }, } @@ -439,7 +454,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( ) }; - let mut scc_annotations = SccAnnotations::init(&definitions); + let mut scc_annotations = SccAnnotations::with_checking_placeholders(&definitions); let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); // This code structure is a bit convoluted because it allows for a planned @@ -475,7 +490,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( ); let (constraint_sccs, scc_annotations) = if added_constraints { - let mut annotations = SccAnnotations::init(&definitions); + let mut annotations = SccAnnotations::without_checking_placeholders(&definitions); // We changed the constraint set and so must recompute SCCs. // Optimisation opportunity: if we can add them incrementally (and that's @@ -583,44 +598,21 @@ fn find_placeholder_mismatch_errors<'tcx>( ) { use NllRegionVariableOrigin::Placeholder; for (rvid, definition) in definitions.iter_enumerated() { - let Placeholder(origin_a) = definition.origin else { + let Placeholder(_) = definition.origin else { continue; }; let scc = sccs.scc(rvid); let annotation = annotations.scc_to_annotation[scc]; - if let Some(cannot_name_rvid) = annotation.reaches_existential_that_cannot_name_us() { - debug!("Existential {cannot_name_rvid:?} lowered our universe..."); - - errors_buffer.push(RegionErrorKind::PlaceholderOutlivesExistentialThatCannotNameIt { + if let Some(illegally_outlived_r) = annotation + .reaches_existential_that_cannot_name_us() + .or_else(|| annotation.reaches_other_placeholder(rvid)) + { + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr: rvid, - existential_that_cannot_name_longer: cannot_name_rvid, - placeholder: origin_a, + illegally_outlived_r, }) } - - let Some(other_placeholder) = annotation.reaches_other_placeholder(rvid) else { - trace!("{rvid:?} reaches no other placeholders"); - continue; - }; - - debug!( - "Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}" - ); - - // FIXME SURELY there is a neater way to do this? - let Placeholder(origin_b) = definitions[other_placeholder].origin else { - unreachable!( - "Region {rvid:?}, {other_placeholder:?} should be placeholders but aren't!" - ); - }; - - errors_buffer.push(RegionErrorKind::PlaceholderOutlivesPlaceholder { - rvid_a: rvid, - rvid_b: other_placeholder, - origin_a, - origin_b, - }); } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5017de8148b92..b52bf212eb503 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2692,24 +2692,30 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } - /// Report that longer_fr: shorter_fr, which doesn't hold, - /// where longer_fr is a placeholder from `placeholder`. + /// Report that `longer_fr: error_vid`, which doesn't hold, + /// where `longer_fr` is a placeholder. fn report_erroneous_rvid_reaches_placeholder( &mut self, longer_fr: RegionVid, - placeholder: ty::Placeholder, error_vid: RegionVid, ) { + use NllRegionVariableOrigin::*; + + let origin_longer = self.regioncx.definitions[longer_fr].origin; + // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - let cause = self - .regioncx - .best_blame_constraint( - longer_fr, - NllRegionVariableOrigin::Placeholder(placeholder), - error_vid, - ) - .0 - .cause; + let cause = + self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause; + + let Placeholder(placeholder) = origin_longer else { + bug!("Expected {longer_fr:?} to come from placeholder!"); + }; + + // FIXME: Is throwing away the existential region really the best here? + let error_region = match self.regioncx.definitions[error_vid].origin { + FreeRegion | Existential { .. } => None, + Placeholder(other_placeholder) => Some(other_placeholder), + }; // FIXME these methods should have better names, and also probably not be this generic. // FIXME note that we *throw away* the error element here! We probably want to @@ -2718,7 +2724,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { self.regioncx.universe_info(placeholder.universe).report_erroneous_element( self, placeholder, - None, + error_region, cause, ); } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index cf5b5ae52b15b..8571d9210dd97 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1249,7 +1249,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { longer_fr, error_element, - placeholder, }); } else { debug!("check_bound_universal_region: all bounds satisfied"); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs index 1265adfad903d..617997db2e0a0 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -56,7 +56,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ) }; - let mut scc_annotations = SccAnnotations::init(&definitions); + let mut scc_annotations = SccAnnotations::without_checking_placeholders(&definitions); let mut constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); let added_constraints = crate::handle_placeholders::rewrite_placeholder_outlives( @@ -67,7 +67,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ); if added_constraints { - scc_annotations = SccAnnotations::init(&definitions); + scc_annotations = SccAnnotations::without_checking_placeholders(&definitions); constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); } From be81d10702ca57aab29771c53f31164cc797165e Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 6 Nov 2025 13:11:11 +0100 Subject: [PATCH 10/17] Simplify error handling & placeholder reachability --- .../rustc_borrowck/src/handle_placeholders.rs | 437 ++++++------------ .../rustc_borrowck/src/region_infer/mod.rs | 11 +- .../region_infer/opaque_types/region_ctxt.rs | 6 +- 3 files changed, 157 insertions(+), 297 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 0181b45ea4e16..3f78fdbc5c682 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -9,7 +9,7 @@ use rustc_index::IndexVec; use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::{RegionVid, UniverseIndex}; -use tracing::{debug, instrument, trace}; +use tracing::{debug, trace}; use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet}; use crate::consumers::OutlivesConstraint; @@ -35,16 +35,8 @@ pub(crate) struct LoweredConstraints<'tcx> { } impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { - pub(crate) fn with_checking_placeholders( - definitions: &'d IndexVec>, - ) -> Self { - Self { scc_to_annotation: IndexVec::new(), definitions, check_placeholders: true } - } - - pub(crate) fn without_checking_placeholders( - definitions: &'d IndexVec>, - ) -> Self { - Self { scc_to_annotation: IndexVec::new(), definitions, check_placeholders: true } + pub(crate) fn init(definitions: &'d IndexVec>) -> Self { + Self { scc_to_annotation: IndexVec::new(), definitions } } } @@ -52,14 +44,11 @@ impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { pub(crate) scc_to_annotation: IndexVec, definitions: &'d IndexVec>, - /// True if we apply extra validity checking logic to placeholder - /// regions, false to skip it. Used only for performance reasons. - check_placeholders: bool, } impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { fn new(&self, element: RegionVid) -> RegionTracker { - RegionTracker::new(element, &self.definitions[element], self.check_placeholders) + RegionTracker::new(element, &self.definitions[element]) } fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { @@ -72,122 +61,24 @@ impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { } #[derive(Copy, Debug, Clone, PartialEq, Eq)] -pub(crate) enum PlaceholderReachability { - /// This SCC reaches no placeholders. - NoPlaceholders, - /// This SCC reaches at least one placeholder. - Placeholders { - /// The largest-universed placeholder we can reach - max_universe: (UniverseIndex, RegionVid), - - /// The placeholder with the smallest ID - min_placeholder: RegionVid, - - /// The placeholder with the largest ID - max_placeholder: RegionVid, - }, +pub(crate) struct PlaceholderReachability { + /// The largest-universed placeholder we can reach + max_universe: (UniverseIndex, RegionVid), + + /// The placeholder with the smallest ID + pub(crate) min_placeholder: RegionVid, + + /// The placeholder with the largest ID + pub(crate) max_placeholder: RegionVid, } impl PlaceholderReachability { /// Merge the reachable placeholders of two graph components. fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability { - use PlaceholderReachability::*; - match (self, other) { - (NoPlaceholders, NoPlaceholders) => NoPlaceholders, - (NoPlaceholders, p @ Placeholders { .. }) - | (p @ Placeholders { .. }, NoPlaceholders) => p, - ( - Placeholders { - min_placeholder: min_pl, - max_placeholder: max_pl, - max_universe: max_u, - }, - Placeholders { min_placeholder, max_placeholder, max_universe }, - ) => Placeholders { - min_placeholder: min_pl.min(min_placeholder), - max_placeholder: max_pl.max(max_placeholder), - max_universe: max_u.max(max_universe), - }, - } - } - - fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> { - match self { - Self::NoPlaceholders => None, - Self::Placeholders { max_universe, .. } => Some(*max_universe), - } - } - - /// If we have reached placeholders, determine if they can - /// be named from this universe. - fn can_be_named_by(&self, from: UniverseIndex) -> bool { - self.max_universe() - .is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe)) - } -} - -#[derive(Copy, Debug, Clone)] - -enum PlaceholderConstraints { - /// The SCC must be able to name this placeholder, in the SCC. - Name(UniverseIndex), - /// Any placeholder we reach must be nameable from this existential. - NameableBy(RegionVid, UniverseIndex), - /// Both constraints at the same time. - NameAndBeNamed { name: UniverseIndex, nameable_by: (RegionVid, UniverseIndex) }, -} - -impl PlaceholderConstraints { - fn add_name(self, name: UniverseIndex) -> Self { - use PlaceholderConstraints::*; - - match self { - Name(universe_index) => Name(universe_index.min(name)), - NameableBy(nb_rvid, nb_u) => NameAndBeNamed { name, nameable_by: (nb_rvid, nb_u) }, - NameAndBeNamed { name: my_name, nameable_by } => { - NameAndBeNamed { name: my_name.min(name), nameable_by } - } - } - } - - fn add_nameable_by(self, region_vid: RegionVid, universe: UniverseIndex) -> Self { - use PlaceholderConstraints::*; - - match self { - Name(universe_index) => { - NameAndBeNamed { name: universe_index, nameable_by: (region_vid, universe) } - } - NameableBy(my_rvid, my_universe_index) => { - let (u, rvid) = ((my_universe_index, my_rvid)).min((universe, region_vid)); - NameableBy(rvid, u) - } - NameAndBeNamed { name, nameable_by } => { - let (u, rvid) = ((nameable_by.1, nameable_by.0)).min((universe, region_vid)); - NameAndBeNamed { name, nameable_by: (rvid, u) } - } - } - } - - fn merge_scc(self, other: Self) -> Self { - use PlaceholderConstraints::*; - match other { - Name(universe_index) | NameAndBeNamed { name: universe_index, .. } => { - self.add_name(universe_index) - } - _ => self, - } - .merge_reached(other) - } - - #[inline(always)] - fn merge_reached(self, reached: Self) -> Self { - use PlaceholderConstraints::*; - match reached { - Name(_) => self, - NameableBy(region_vid, universe_index) - | NameAndBeNamed { nameable_by: (region_vid, universe_index), .. } => { - self.add_nameable_by(region_vid, universe_index) - } + PlaceholderReachability { + max_universe: self.max_universe.max(other.max_universe), + min_placeholder: self.min_placeholder.min(other.min_placeholder), + max_placeholder: self.max_placeholder.max(other.max_placeholder), } } } @@ -196,28 +87,26 @@ impl PlaceholderConstraints { /// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] pub(crate) struct RegionTracker { - pub(crate) reachable_placeholders: PlaceholderReachability, + pub(crate) reachable_placeholders: Option, /// The smallest max nameable universe of all /// regions reachable from this SCC. min_max_nameable_universe: UniverseIndex, - /// State tracking for exceptional circumstances. - exception: Option, + /// Does this SCC contain a placeholder region? + is_placeholder: Option<(UniverseIndex, RegionVid)>, + + /// The worst-nameable (max univers'd) existential region + /// we reach. + worst_existential: Option<(UniverseIndex, RegionVid)>, /// The representative Region Variable Id for this SCC. pub(crate) representative: Representative, } impl RegionTracker { - pub(crate) fn new( - rvid: RegionVid, - definition: &RegionDefinition<'_>, - check_placeholders: bool, - ) -> Self { + pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { use NllRegionVariableOrigin::*; - use PlaceholderConstraints::*; - use PlaceholderReachability::*; let min_max_nameable_universe = definition.universe; let representative = Representative::new(rvid, definition); @@ -225,25 +114,28 @@ impl RegionTracker { match definition.origin { FreeRegion => Self { - reachable_placeholders: NoPlaceholders, + reachable_placeholders: None, min_max_nameable_universe, - exception: None, + is_placeholder: None, + worst_existential: None, representative, }, Placeholder(_) => Self { - reachable_placeholders: Placeholders { + reachable_placeholders: Some(PlaceholderReachability { max_universe: universe_and_rvid, min_placeholder: rvid, max_placeholder: rvid, - }, + }), min_max_nameable_universe, - exception: check_placeholders.then_some(Name(definition.universe)), + is_placeholder: Some(universe_and_rvid), + worst_existential: None, representative, }, Existential { .. } => Self { - reachable_placeholders: NoPlaceholders, + reachable_placeholders: None, min_max_nameable_universe, - exception: check_placeholders.then_some(NameableBy(rvid, definition.universe)), + is_placeholder: None, + worst_existential: Some(universe_and_rvid), representative, }, } @@ -258,108 +150,65 @@ impl RegionTracker { } pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { - if let Some((universe, _)) = self.reachable_placeholders.max_universe() { - universe - } else { - UniverseIndex::ROOT - } + self.reachable_placeholders.map(|p| p.max_universe.0).unwrap_or(UniverseIndex::ROOT) } /// Determine if we can name all the placeholders in `other`. pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { - other.reachable_placeholders.can_be_named_by(self.min_max_nameable_universe) - } - - /// If this SCC reaches a placeholder it can't name, return it. - fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> { - self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| { - !self.min_max_nameable_universe.can_name(placeholder_universe) - }) - } - - /// Check for placeholder leaks where a placeholder `'p` outlives (transitively) - /// an existential `'e` and `'e` cannot name `'p`. This is sort of a dual of - /// `unnameable_placeholder`; one of the members of this SCC cannot be named by - /// the SCC itself. - /// - /// Returns *a* culprit (there may be more than one). - fn reaches_existential_that_cannot_name_us(&self) -> Option { - if let Some(PlaceholderConstraints::NameAndBeNamed { - name: p_u, - nameable_by: (ex_rv, ex_u), - }) = self.exception - && ex_u.cannot_name(p_u) - { - Some(ex_rv) - } else { - None - } - } - - /// Determine if this SCC reaches a placeholder that isn't `placeholder_rvid`, - /// returning it if that is the case. This prefers the placeholder with the - /// smallest region variable ID. - fn reaches_other_placeholder(&self, placeholder_rvid: RegionVid) -> Option { - match self.reachable_placeholders { - PlaceholderReachability::NoPlaceholders => None, - PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. } - if min_placeholder == max_placeholder => - { - None - } - PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. } - if min_placeholder == placeholder_rvid => - { - Some(max_placeholder) - } - PlaceholderReachability::Placeholders { min_placeholder, .. } => Some(min_placeholder), - } + self.min_max_nameable_universe.can_name(other.max_placeholder_universe_reached()) } /// If this SCC reaches at least one placeholder, return /// its region vid. If there's more than one, return the one /// with the smallest vid. pub(crate) fn reached_placeholder(&self) -> Option { - match self.reachable_placeholders { - PlaceholderReachability::NoPlaceholders => None, - PlaceholderReachability::Placeholders { min_placeholder, .. } => Some(min_placeholder), - } + self.reachable_placeholders.map(|p| p.min_placeholder) + } +} + +#[inline(always)] +fn either_or_merge(a: Option, b: Option, merge: fn(T, T) -> T) -> Option { + match (a, b) { + (None, x) | (x, None) => x, + (Some(a), Some(b)) => Some(merge(a, b)), } } impl scc::Annotation for RegionTracker { fn merge_scc(self, other: Self) -> Self { trace!("{:?} << {:?}", self.representative, other.representative); - Self { representative: self.representative.min(other.representative), - exception: match (self.exception, other.exception) { - (None, other) | (other, None) => other, - (Some(ours), Some(theirs)) => Some(ours.merge_scc(theirs)), - }, + is_placeholder: either_or_merge( + self.is_placeholder, + other.is_placeholder, + |ours, theirs| { + let (u1, r1) = ours; + let (u2, r2) = theirs; + + if u1 == u2 { (u1, r1.min(r2)) } else { ours.min(theirs) } + }, + ), ..self.merge_reached(other) } } #[inline(always)] fn merge_reached(self, other: Self) -> Self { - use PlaceholderConstraints::*; Self { - exception: match (self.exception, other.exception) { - // Propagate only reachability (nameable by). - (None, None) => None, - (None, Some(NameableBy(r, u))) - | (None, Some(NameAndBeNamed { nameable_by: (r, u), .. })) => { - Some(NameableBy(r, u)) - } - (Some(_), None) | (None, Some(Name(_))) => self.exception, - (Some(this), Some(that)) => Some(this.merge_reached(that)), - }, + worst_existential: self + .worst_existential + .xor(other.worst_existential) + .or_else(|| self.worst_existential.min(other.worst_existential)), min_max_nameable_universe: self .min_max_nameable_universe .min(other.min_max_nameable_universe), - reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders), - representative: self.representative, + reachable_placeholders: either_or_merge( + self.reachable_placeholders, + other.reachable_placeholders, + |ours, theirs| ours.merge(theirs), + ), + ..self } } } @@ -454,7 +303,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( ) }; - let mut scc_annotations = SccAnnotations::with_checking_placeholders(&definitions); + let mut scc_annotations = SccAnnotations::init(&definitions); let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); // This code structure is a bit convoluted because it allows for a planned @@ -480,17 +329,11 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( &scc_annotations, fr_static, &mut outlives_constraints, - ); - - find_placeholder_mismatch_errors( - &definitions, - &constraint_sccs, - &scc_annotations, errors_buffer, ); let (constraint_sccs, scc_annotations) = if added_constraints { - let mut annotations = SccAnnotations::without_checking_placeholders(&definitions); + let mut annotations = SccAnnotations::init(&definitions); // We changed the constraint set and so must recompute SCCs. // Optimisation opportunity: if we can add them incrementally (and that's @@ -519,6 +362,7 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( annotations: &SccAnnotations<'_, '_, RegionTracker>, fr_static: RegionVid, outlives_constraints: &mut OutlivesConstraintSet<'tcx>, + errors_buffer: &mut RegionErrors<'tcx>, ) -> bool { // Changed to `true` if we added any constraints and need to // recompute SCCs. @@ -536,83 +380,98 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( let annotation = annotations[scc]; - let Some((unnameable_u, unnameable_placeholder)) = annotation.unnameable_placeholder() + let Some(PlaceholderReachability { + min_placeholder, + max_placeholder, + max_universe: (max_placeholder_u, max_placeholder_u_rvid), + }) = annotation.reachable_placeholders else { + debug!("No placeholders reached from {scc:?}"); continue; }; + if let Some((_, us)) = annotation.is_placeholder + && min_placeholder != max_placeholder + { + let illegally_outlived_r = + if min_placeholder == us { max_placeholder } else { min_placeholder }; + debug!("Placeholder {us:?} outlives placeholder {illegally_outlived_r:?}"); + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { + longer_fr: us, + illegally_outlived_r, + }); + } + + if annotation.min_max_nameable_universe.can_name(max_placeholder_u) { + debug!("All placeholders nameable from {scc:?}!"); + continue; + } + debug!( - "Placeholder {unnameable_placeholder:?} with universe {unnameable_u:?} unnameable from {scc:?} represented by {:?}", + "Placeholder {max_placeholder_u_rvid:?} with universe {max_placeholder_u_rvid:?} unnameable from {scc:?} represented by {:?}", annotation.representative ); - let representative_rvid = annotation.representative.rvid(); + // Figure out if we had our universe lowered by an existential + // that cannot name us, a placeholder. This is an error. + if let Some((ex_u, ex_r)) = annotation.worst_existential + && let Some((pl_u, pl_r)) = annotation.is_placeholder + && ex_u.cannot_name(pl_u) + { + debug!("{pl_r:?} outlives existential {ex_r:?} that cannot name it!"); + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { + longer_fr: pl_r, + illegally_outlived_r: ex_r, + }); + + if annotation.representative.rvid() != max_placeholder_u_rvid { + // We *additionally* have a case of an unnameable placeholder. Force the SCC + // to 'static to maintain compatibility with previous error reporting. + // Note: not required for correctness, since we already have an error and can't + // save the situation. + added_constraints = true; + outlives_constraints.push(OutlivesConstraint { + sup: annotation.representative.rvid(), + sub: fr_static, + category: ConstraintCategory::OutlivesUnnameablePlaceholder( + max_placeholder_u_rvid, + ), + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }); + } + } else { + // This SCC outlives a placeholder it can't name and must outlive 'static. - // If we got here, our representative is a placeholder and it reaches some - // region that can't name it. That's a separate error! - if representative_rvid == unnameable_placeholder { - debug!( - "No need to add constraints for a placeholder reaching an existential that can't name it; that's a separate error." - ); - assert!( - matches!(annotation.representative, Representative::Placeholder(_)), - "Representative wasn't a placeholder, which should not be possible!" - ); - continue; - } + let representative_rvid = annotation.representative.rvid(); - // FIXME: if we can extract a useful blame span here, future error - // reporting and constraint search can be simplified. - - added_constraints = true; - outlives_constraints.push(OutlivesConstraint { - sup: representative_rvid, - sub: fr_static, - category: ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable_placeholder), - locations: Locations::All(rustc_span::DUMMY_SP), - span: rustc_span::DUMMY_SP, - variance_info: VarianceDiagInfo::None, - from_closure: false, - }); - } - added_constraints -} + if matches!(annotation.representative, Representative::Placeholder(_)) { + // If this SCC is a placeholder, we fully describe what's + // wrong with a placeholder-reaches-placeholder error. + continue; + } -/// Identify errors where placeholders illegally reach other regions, and generate -/// errors stored into `errors_buffer`. -/// -/// There are two sources of such errors: -/// 1. A placeholder reaches (possibly transitively) another placeholder. -/// 2. A placeholder `p` reaches (possibly transitively) an existential `e`, -/// where `e` has an allowed maximum universe smaller than `p`'s. -/// -/// There are other potential placeholder errors, but those are detected after -/// region inference, since it may apply type tests or member constraints that -/// alter the contents of SCCs and thus can't be detected at this point. -#[instrument(skip(definitions, sccs, annotations, errors_buffer), level = "debug")] -fn find_placeholder_mismatch_errors<'tcx>( - definitions: &IndexVec>, - sccs: &Sccs, - annotations: &SccAnnotations<'_, '_, RegionTracker>, - errors_buffer: &mut RegionErrors<'tcx>, -) { - use NllRegionVariableOrigin::Placeholder; - for (rvid, definition) in definitions.iter_enumerated() { - let Placeholder(_) = definition.origin else { - continue; - }; + // FIXME: if we can extract a useful blame span here, future error + // reporting and constraint search can be simplified. - let scc = sccs.scc(rvid); - let annotation = annotations.scc_to_annotation[scc]; + assert!( + representative_rvid != max_placeholder_u_rvid, + "The representative should never be unnameable!" + ); - if let Some(illegally_outlived_r) = annotation - .reaches_existential_that_cannot_name_us() - .or_else(|| annotation.reaches_other_placeholder(rvid)) - { - errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { - longer_fr: rvid, - illegally_outlived_r, - }) + added_constraints = true; + outlives_constraints.push(OutlivesConstraint { + sup: representative_rvid, + sub: fr_static, + category: ConstraintCategory::OutlivesUnnameablePlaceholder(max_placeholder_u_rvid), + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }); } } + added_constraints } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 8571d9210dd97..c7dd96dda0479 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -26,7 +26,7 @@ use crate::constraints::graph::NormalConstraintGraph; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::handle_placeholders::{LoweredConstraints, PlaceholderReachability, RegionTracker}; +use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; @@ -1473,11 +1473,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, scc: ConstraintSccIndex, ) -> Option { - if let PlaceholderReachability::Placeholders { min_placeholder, max_placeholder, .. } = - self.scc_annotations[scc].reachable_placeholders - && min_placeholder == max_placeholder - && let NllRegionVariableOrigin::Placeholder(p) = - self.definitions[min_placeholder].origin + let ps = self.scc_annotations[scc].reachable_placeholders?; + + if let NllRegionVariableOrigin::Placeholder(p) = self.definitions[ps.min_placeholder].origin + && ps.min_placeholder == ps.max_placeholder { Some(p) } else { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs index 617997db2e0a0..2e8d66d81daa4 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -8,6 +8,7 @@ use rustc_mir_dataflow::points::DenseLocationMap; use crate::BorrowckInferCtxt; use crate::constraints::ConstraintSccIndex; +use crate::diagnostics::RegionErrors; use crate::handle_placeholders::{SccAnnotations, region_definitions}; use crate::region_infer::reverse_sccs::ReverseSccGraph; use crate::region_infer::values::RegionValues; @@ -56,7 +57,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ) }; - let mut scc_annotations = SccAnnotations::without_checking_placeholders(&definitions); + let mut scc_annotations = SccAnnotations::init(&definitions); let mut constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); let added_constraints = crate::handle_placeholders::rewrite_placeholder_outlives( @@ -64,10 +65,11 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { &scc_annotations, universal_regions.fr_static, &mut outlives_constraints, + &mut RegionErrors::new(infcx.tcx), ); if added_constraints { - scc_annotations = SccAnnotations::without_checking_placeholders(&definitions); + scc_annotations = SccAnnotations::init(&definitions); constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); } From 42bac69ed019a1b1a4a751a54d801494f9c7ac4a Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Fri, 7 Nov 2025 21:51:46 +0100 Subject: [PATCH 11/17] Simplify some of the error handling for placeholder errors --- .../src/diagnostics/region_errors.rs | 23 +-- .../rustc_borrowck/src/handle_placeholders.rs | 191 +++++++++--------- .../rustc_borrowck/src/region_infer/mod.rs | 24 +-- .../region_infer/opaque_types/region_ctxt.rs | 1 + 4 files changed, 112 insertions(+), 127 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3aa5f8b8b608e..7eba0d0b89c8f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -29,7 +29,6 @@ use tracing::{debug, instrument, trace}; use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::nll::ConstraintDescription; -use crate::region_infer::values::RegionElement; use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, @@ -104,20 +103,10 @@ pub(crate) enum RegionErrorKind<'tcx> { /// A generic bound failure for a type test (`T: 'a`). TypeTestError { type_test: TypeTest<'tcx> }, - /// 'a outlives 'b, which does not hold. 'a is always a - /// placeholder and 'b is either an existential that cannot name - /// 'a, or another placeholder. + /// 'p outlives 'r, which does not hold. 'p is always a placeholder + /// and 'r is some other region. PlaceholderOutlivesIllegalRegion { longer_fr: RegionVid, illegally_outlived_r: RegionVid }, - /// Higher-ranked subtyping error. A placeholder outlives - /// either a location or a universal region. - PlaceholderOutlivesLocationOrUniversal { - /// The placeholder free region. - longer_fr: RegionVid, - /// The region element that erroneously must be outlived by `longer_fr`. - error_element: RegionElement, - }, - /// Any other lifetime error. RegionError { /// The origin of the region. @@ -340,14 +329,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { - longer_fr, - error_element, - } => self.report_erroneous_rvid_reaches_placeholder( - longer_fr, - self.regioncx.region_from_element(longer_fr, &error_element), - ), - RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, illegally_outlived_r, diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 3f78fdbc5c682..9527773e0c38d 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -1,6 +1,8 @@ //! Logic for lowering higher-kinded outlives constraints //! (with placeholders and universes) and turn them into regular //! outlives constraints. +use std::cmp; + use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::scc; @@ -63,7 +65,7 @@ impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { #[derive(Copy, Debug, Clone, PartialEq, Eq)] pub(crate) struct PlaceholderReachability { /// The largest-universed placeholder we can reach - max_universe: (UniverseIndex, RegionVid), + max_universe: RegionWithUniverse, /// The placeholder with the smallest ID pub(crate) min_placeholder: RegionVid, @@ -83,6 +85,30 @@ impl PlaceholderReachability { } } +/// A region with its universe, ordered fist by largest unverse, then +/// by smallest region (reverse region id order). +#[derive(Copy, Debug, Clone, PartialEq, Eq)] +struct RegionWithUniverse { + u: UniverseIndex, + r: RegionVid, +} + +impl Ord for RegionWithUniverse { + fn cmp(&self, other: &Self) -> cmp::Ordering { + if self.u.cmp(&other.u) == cmp::Ordering::Equal { + self.r.cmp(&other.r).reverse() + } else { + self.u.cmp(&other.u) + } + } +} + +impl PartialOrd for RegionWithUniverse { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + /// An annotation for region graph SCCs that tracks /// the values of its elements. This annotates a single SCC. #[derive(Copy, Debug, Clone)] @@ -93,11 +119,10 @@ pub(crate) struct RegionTracker { /// regions reachable from this SCC. min_max_nameable_universe: UniverseIndex, - /// Does this SCC contain a placeholder region? - is_placeholder: Option<(UniverseIndex, RegionVid)>, + /// The worst-nameable (highest univers'd) placeholder region in this SCC. + is_placeholder: Option, - /// The worst-nameable (max univers'd) existential region - /// we reach. + /// The worst-naming (min univers'd) existential region we reach. worst_existential: Option<(UniverseIndex, RegionVid)>, /// The representative Region Variable Id for this SCC. @@ -110,7 +135,7 @@ impl RegionTracker { let min_max_nameable_universe = definition.universe; let representative = Representative::new(rvid, definition); - let universe_and_rvid = (definition.universe, rvid); + let universe_and_rvid = RegionWithUniverse { r: rvid, u: definition.universe }; match definition.origin { FreeRegion => Self { @@ -135,7 +160,7 @@ impl RegionTracker { reachable_placeholders: None, min_max_nameable_universe, is_placeholder: None, - worst_existential: Some(universe_and_rvid), + worst_existential: Some((definition.universe, rvid)), representative, }, } @@ -150,7 +175,7 @@ impl RegionTracker { } pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { - self.reachable_placeholders.map(|p| p.max_universe.0).unwrap_or(UniverseIndex::ROOT) + self.reachable_placeholders.map(|p| p.max_universe.u).unwrap_or(UniverseIndex::ROOT) } /// Determine if we can name all the placeholders in `other`. @@ -166,29 +191,12 @@ impl RegionTracker { } } -#[inline(always)] -fn either_or_merge(a: Option, b: Option, merge: fn(T, T) -> T) -> Option { - match (a, b) { - (None, x) | (x, None) => x, - (Some(a), Some(b)) => Some(merge(a, b)), - } -} - impl scc::Annotation for RegionTracker { fn merge_scc(self, other: Self) -> Self { trace!("{:?} << {:?}", self.representative, other.representative); Self { representative: self.representative.min(other.representative), - is_placeholder: either_or_merge( - self.is_placeholder, - other.is_placeholder, - |ours, theirs| { - let (u1, r1) = ours; - let (u2, r2) = theirs; - - if u1 == u2 { (u1, r1.min(r2)) } else { ours.min(theirs) } - }, - ), + is_placeholder: self.is_placeholder.max(other.is_placeholder), ..self.merge_reached(other) } } @@ -203,11 +211,13 @@ impl scc::Annotation for RegionTracker { min_max_nameable_universe: self .min_max_nameable_universe .min(other.min_max_nameable_universe), - reachable_placeholders: either_or_merge( + reachable_placeholders: match ( self.reachable_placeholders, other.reachable_placeholders, - |ours, theirs| ours.merge(theirs), - ), + ) { + (None, x) | (x, None) => x, + (Some(ours), Some(theirs)) => Some(ours.merge(theirs)), + }, ..self } } @@ -330,6 +340,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( fr_static, &mut outlives_constraints, errors_buffer, + &definitions, ); let (constraint_sccs, scc_annotations) = if added_constraints { @@ -363,6 +374,7 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( fr_static: RegionVid, outlives_constraints: &mut OutlivesConstraintSet<'tcx>, errors_buffer: &mut RegionErrors<'tcx>, + definitions: &IndexVec>, ) -> bool { // Changed to `true` if we added any constraints and need to // recompute SCCs. @@ -380,98 +392,97 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( let annotation = annotations[scc]; - let Some(PlaceholderReachability { - min_placeholder, - max_placeholder, - max_universe: (max_placeholder_u, max_placeholder_u_rvid), - }) = annotation.reachable_placeholders + let Some(PlaceholderReachability { min_placeholder, max_placeholder, max_universe }) = + annotation.reachable_placeholders else { debug!("No placeholders reached from {scc:?}"); continue; }; - if let Some((_, us)) = annotation.is_placeholder + if let Some(us) = annotation.is_placeholder && min_placeholder != max_placeholder { let illegally_outlived_r = - if min_placeholder == us { max_placeholder } else { min_placeholder }; + if min_placeholder == us.r { max_placeholder } else { min_placeholder }; debug!("Placeholder {us:?} outlives placeholder {illegally_outlived_r:?}"); errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { - longer_fr: us, + longer_fr: us.r, illegally_outlived_r, }); + // FIXME: investigate if it's an actual improvement to drop early here + // and stop reporting errors for this SCC since we are guaranteed to + // have at least one. } - if annotation.min_max_nameable_universe.can_name(max_placeholder_u) { + if annotation.min_max_nameable_universe.can_name(max_universe.u) { debug!("All placeholders nameable from {scc:?}!"); continue; } debug!( - "Placeholder {max_placeholder_u_rvid:?} with universe {max_placeholder_u_rvid:?} unnameable from {scc:?} represented by {:?}", + "Placeholder {max_universe:?} unnameable from {scc:?} represented by {:?}", annotation.representative ); // Figure out if we had our universe lowered by an existential // that cannot name us, a placeholder. This is an error. if let Some((ex_u, ex_r)) = annotation.worst_existential - && let Some((pl_u, pl_r)) = annotation.is_placeholder - && ex_u.cannot_name(pl_u) + && let Some(pl) = annotation.is_placeholder + && ex_u.cannot_name(pl.u) { - debug!("{pl_r:?} outlives existential {ex_r:?} that cannot name it!"); + debug!("{pl:?} outlives existential {ex_r:?} that cannot name it!"); + // Prefer the representative region if it's also unnameable. + let longer_fr = if let Representative::Placeholder(p) = annotation.representative + && ex_u.cannot_name(definitions[p].universe) + { + p + } else { + pl.r + }; errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { - longer_fr: pl_r, + longer_fr, illegally_outlived_r: ex_r, }); + // FIXME: we could `continue` here since there is no point in adding + // 'r: 'static for this SCC now that it's already outlived an + // existential it shouldn't, but we do anyway for compatibility with + // earlier versions' output. + }; - if annotation.representative.rvid() != max_placeholder_u_rvid { - // We *additionally* have a case of an unnameable placeholder. Force the SCC - // to 'static to maintain compatibility with previous error reporting. - // Note: not required for correctness, since we already have an error and can't - // save the situation. - added_constraints = true; - outlives_constraints.push(OutlivesConstraint { - sup: annotation.representative.rvid(), - sub: fr_static, - category: ConstraintCategory::OutlivesUnnameablePlaceholder( - max_placeholder_u_rvid, - ), - locations: Locations::All(rustc_span::DUMMY_SP), - span: rustc_span::DUMMY_SP, - variance_info: VarianceDiagInfo::None, - from_closure: false, - }); - } - } else { - // This SCC outlives a placeholder it can't name and must outlive 'static. - - let representative_rvid = annotation.representative.rvid(); - - if matches!(annotation.representative, Representative::Placeholder(_)) { - // If this SCC is a placeholder, we fully describe what's - // wrong with a placeholder-reaches-placeholder error. - continue; - } - - // FIXME: if we can extract a useful blame span here, future error - // reporting and constraint search can be simplified. - - assert!( - representative_rvid != max_placeholder_u_rvid, - "The representative should never be unnameable!" - ); - - added_constraints = true; - outlives_constraints.push(OutlivesConstraint { - sup: representative_rvid, - sub: fr_static, - category: ConstraintCategory::OutlivesUnnameablePlaceholder(max_placeholder_u_rvid), - locations: Locations::All(rustc_span::DUMMY_SP), - span: rustc_span::DUMMY_SP, - variance_info: VarianceDiagInfo::None, - from_closure: false, - }); + let representative_rvid = annotation.representative.rvid(); + + if representative_rvid == max_universe.r { + assert!(matches!(annotation.representative, Representative::Placeholder(_))); + // The unnameable placeholder *is* the representative. + // If this SCC is represented by a placeholder `p` which cannot be named, + // from its own SCC, `p` must have at some point reached a/an: + // - existential region that could not name it (the `if` above) + // - free region that lowered its universe (will flag an error in region + // inference since `p` isn't empty) + // - another placeholder (will flag an error above, but will reach here). + // + // To avoid adding the invalid constraint "'p: 'static` due to `'p` being + // unnameable from the SCC represented by `'p`", we nope out early here + // at no risk of soundness issues since at this point all paths lead + // to an error. + continue; } + + // This SCC outlives a placeholder it can't name and must outlive 'static. + + // FIXME: if we can extract a useful blame span here, future error + // reporting and constraint search can be simplified. + + added_constraints = true; + outlives_constraints.push(OutlivesConstraint { + sup: representative_rvid, + sub: fr_static, + category: ConstraintCategory::OutlivesUnnameablePlaceholder(max_universe.r), + locations: Locations::All(rustc_span::DUMMY_SP), + span: rustc_span::DUMMY_SP, + variance_info: VarianceDiagInfo::None, + from_closure: false, + }); } added_constraints } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c7dd96dda0479..6c09663b273fd 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1246,9 +1246,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // else about it! if let Some(error_element) = self.scc_values.elements_contained_in(longer_fr_scc).next() { // Stop after the first error, it gets too noisy otherwise, and does not provide more information. - errors_buffer.push(RegionErrorKind::PlaceholderOutlivesLocationOrUniversal { + let illegally_outlived_r = match error_element { + RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), + RegionElement::RootUniversalRegion(r) => r, + }; + errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion { longer_fr, - error_element, + illegally_outlived_r, }); } else { debug!("check_bound_universal_region: all bounds satisfied"); @@ -1436,18 +1440,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { }, true).unwrap().1 } - /// Get the region outlived by `longer_fr` and live at `element`. - pub(crate) fn region_from_element( - &self, - longer_fr: RegionVid, - element: &RegionElement, - ) -> RegionVid { - match *element { - RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), - RegionElement::RootUniversalRegion(r) => r, - } - } - /// Get the region definition of `r`. pub(crate) fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> { &self.definitions[r] @@ -1459,8 +1451,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, scc: ConstraintSccIndex, ) -> Option { - if let Representative::Placeholder(r) = self.scc_annotations[scc].representative - && let NllRegionVariableOrigin::Placeholder(p) = self.definitions[r].origin + if let NllRegionVariableOrigin::Placeholder(p) = + self.definitions[self.scc_annotations[scc].representative.rvid()].origin { Some(p) } else { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs index 2e8d66d81daa4..4cc11ba1e19cf 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -66,6 +66,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { universal_regions.fr_static, &mut outlives_constraints, &mut RegionErrors::new(infcx.tcx), + &definitions, ); if added_constraints { From e8c78348a09a69f09bdca5022d7f1e61f9e4ee4e Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 10 Nov 2025 12:12:22 +0100 Subject: [PATCH 12/17] Switch SCC annotations to a update style; it's more ergonomic --- .../rustc_borrowck/src/handle_placeholders.rs | 53 ++++++++----------- .../src/graph/scc/mod.rs | 28 +++------- .../src/graph/scc/tests.rs | 17 +++--- 3 files changed, 37 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 9527773e0c38d..2e0adb1f71f82 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -76,12 +76,10 @@ pub(crate) struct PlaceholderReachability { impl PlaceholderReachability { /// Merge the reachable placeholders of two graph components. - fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability { - PlaceholderReachability { - max_universe: self.max_universe.max(other.max_universe), - min_placeholder: self.min_placeholder.min(other.min_placeholder), - max_placeholder: self.max_placeholder.max(other.max_placeholder), - } + fn merge(&mut self, other: &PlaceholderReachability) { + self.max_universe = self.max_universe.max(other.max_universe); + self.min_placeholder = self.min_placeholder.min(other.min_placeholder); + self.max_placeholder = self.max_placeholder.max(other.max_placeholder); } } @@ -192,34 +190,26 @@ impl RegionTracker { } impl scc::Annotation for RegionTracker { - fn merge_scc(self, other: Self) -> Self { + fn update_scc(&mut self, other: &Self) { trace!("{:?} << {:?}", self.representative, other.representative); - Self { - representative: self.representative.min(other.representative), - is_placeholder: self.is_placeholder.max(other.is_placeholder), - ..self.merge_reached(other) - } + self.representative = self.representative.min(other.representative); + self.is_placeholder = self.is_placeholder.max(other.is_placeholder); + self.update_reachable(other); // SCC membership implies reachability. } #[inline(always)] - fn merge_reached(self, other: Self) -> Self { - Self { - worst_existential: self - .worst_existential - .xor(other.worst_existential) - .or_else(|| self.worst_existential.min(other.worst_existential)), - min_max_nameable_universe: self - .min_max_nameable_universe - .min(other.min_max_nameable_universe), - reachable_placeholders: match ( - self.reachable_placeholders, - other.reachable_placeholders, - ) { - (None, x) | (x, None) => x, - (Some(ours), Some(theirs)) => Some(ours.merge(theirs)), - }, - ..self - } + fn update_reachable(&mut self, other: &Self) { + self.worst_existential = self + .worst_existential + .xor(other.worst_existential) + .or_else(|| self.worst_existential.min(other.worst_existential)); + self.min_max_nameable_universe = + self.min_max_nameable_universe.min(other.min_max_nameable_universe); + match (self.reachable_placeholders.as_mut(), other.reachable_placeholders.as_ref()) { + (None, None) | (Some(_), None) => (), + (None, Some(theirs)) => self.reachable_placeholders = Some(*theirs), + (Some(ours), Some(theirs)) => ours.merge(theirs), + }; } } @@ -461,13 +451,12 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( // inference since `p` isn't empty) // - another placeholder (will flag an error above, but will reach here). // - // To avoid adding the invalid constraint "'p: 'static` due to `'p` being + // To avoid adding the invalid constraint "`'p: 'static` due to `'p` being // unnameable from the SCC represented by `'p`", we nope out early here // at no risk of soundness issues since at this point all paths lead // to an error. continue; } - // This SCC outlives a placeholder it can't name and must outlive 'static. // FIXME: if we can extract a useful blame span here, future error diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 1882e6e835a4e..33b5cddf425a5 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -27,26 +27,18 @@ mod tests; /// the max/min element of the SCC, or all of the above. /// /// Concretely, the both merge operations must commute, e.g. where `merge` -/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)` +/// is `update_scc` and `update_reached`: `a.merge(b) == b.merge(a)` /// /// In general, what you want is probably always min/max according /// to some ordering, potentially with side constraints (min x such /// that P holds). pub trait Annotation: Debug + Copy { /// Merge two existing annotations into one during - /// path compression.o - fn merge_scc(self, other: Self) -> Self; + /// path compression. + fn update_scc(&mut self, other: &Self); /// Merge a successor into this annotation. - fn merge_reached(self, other: Self) -> Self; - - fn update_scc(&mut self, other: Self) { - *self = self.merge_scc(other) - } - - fn update_reachable(&mut self, other: Self) { - *self = self.merge_reached(other) - } + fn update_reachable(&mut self, other: &Self); } /// An accumulator for annotations. @@ -70,12 +62,8 @@ impl Annotations for NoAnnotations { /// The empty annotation, which does nothing. impl Annotation for () { - fn merge_reached(self, _other: Self) -> Self { - () - } - fn merge_scc(self, _other: Self) -> Self { - () - } + fn update_reachable(&mut self, _other: &Self) {} + fn update_scc(&mut self, _other: &Self) {} } /// Strongly connected components (SCC) of a graph. The type `N` is @@ -614,7 +602,7 @@ where *min_depth = successor_min_depth; *min_cycle_root = successor_node; } - current_component_annotation.update_scc(successor_annotation); + current_component_annotation.update_scc(&successor_annotation); } // The starting node `node` is succeeded by a fully identified SCC // which is now added to the set under `scc_index`. @@ -629,7 +617,7 @@ where // the `successors_stack` for later. trace!(?node, ?successor_scc_index); successors_stack.push(successor_scc_index); - current_component_annotation.update_reachable(successor_annotation); + current_component_annotation.update_reachable(&successor_annotation); } // `node` has no more (direct) successors; search recursively. None => { diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 8f04baf51f355..4626861c3e00b 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -32,12 +32,12 @@ impl Maxes { } impl Annotation for MaxReached { - fn merge_scc(self, other: Self) -> Self { - Self(std::cmp::max(other.0, self.0)) + fn update_scc(&mut self, other: &Self) { + self.0 = self.0.max(other.0); } - fn merge_reached(self, other: Self) -> Self { - Self(std::cmp::max(other.0, self.0)) + fn update_reachable(&mut self, other: &Self) { + self.0 = self.0.max(other.0); } } @@ -75,13 +75,12 @@ impl Annotations for MinMaxes { } impl Annotation for MinMaxIn { - fn merge_scc(self, other: Self) -> Self { - Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) } + fn update_scc(&mut self, other: &Self) { + self.min = self.min.min(other.min); + self.max = self.max.max(other.max); } - fn merge_reached(self, _other: Self) -> Self { - self - } + fn update_reachable(&mut self, _other: &Self) {} } #[test] From 01158183c1f8132a6e16d61750e150e4cd1f97b0 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 10 Nov 2025 12:56:18 +0100 Subject: [PATCH 13/17] Some more code cleanup --- compiler/rustc_borrowck/src/handle_placeholders.rs | 14 +------------- compiler/rustc_borrowck/src/region_infer/mod.rs | 10 +++++----- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 2e0adb1f71f82..6ffe439813ece 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -175,18 +175,6 @@ impl RegionTracker { pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { self.reachable_placeholders.map(|p| p.max_universe.u).unwrap_or(UniverseIndex::ROOT) } - - /// Determine if we can name all the placeholders in `other`. - pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { - self.min_max_nameable_universe.can_name(other.max_placeholder_universe_reached()) - } - - /// If this SCC reaches at least one placeholder, return - /// its region vid. If there's more than one, return the one - /// with the smallest vid. - pub(crate) fn reached_placeholder(&self) -> Option { - self.reachable_placeholders.map(|p| p.min_placeholder) - } } impl scc::Annotation for RegionTracker { @@ -405,7 +393,7 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( } if annotation.min_max_nameable_universe.can_name(max_universe.u) { - debug!("All placeholders nameable from {scc:?}!"); + trace!("All placeholders nameable from {scc:?}!"); continue; } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 6c09663b273fd..ede1e69af9af6 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -499,7 +499,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_a: ConstraintSccIndex, scc_b: ConstraintSccIndex, ) -> bool { - self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b]) + self.scc_annotations[scc_a] + .max_nameable_universe() + .can_name(self.scc_annotations[scc_b].max_placeholder_universe_reached()) } /// Once regions have been propagated, this method is used to see @@ -611,10 +613,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // It doesn't matter *what* universe because the promoted `T` will // always be in the root universe. - if let Some(min_placeholder) = self.scc_annotations[r_scc].reached_placeholder() { - debug!( - "encountered placeholder in higher universe: {min_placeholder:?}, requiring 'static" - ); + if !self.scc_annotations[r_scc].max_placeholder_universe_reached().is_root() { + debug!("encountered placeholder in higher universe, requiring 'static"); let static_r = self.universal_regions().fr_static; propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject, From 38e472ebb25154ad73efb929fe21a30ea2f45b4d Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 10 Nov 2025 14:02:29 +0100 Subject: [PATCH 14/17] Remove option use according to review comments --- .../src/diagnostics/region_errors.rs | 28 +++++++++++++------ .../rustc_borrowck/src/handle_placeholders.rs | 4 +-- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 7eba0d0b89c8f..75bc44e2bb688 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -194,23 +194,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { &self, diag: &mut Diag<'_>, lower_bound: RegionVid, - ) -> Option<()> { + ) { let tcx = self.infcx.tcx; // find generic associated types in the given region 'lower_bound' let scc = self.regioncx.constraint_sccs().scc(lower_bound); - let placeholder: ty::PlaceholderRegion = self.regioncx.placeholder_representative(scc)?; - let placeholder_id = placeholder.bound.kind.get_id()?.as_local()?; - let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id); - let generics_impl = - self.infcx.tcx.parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id)).generics()?; + let Some(gat_hir_id) = self + .regioncx + .placeholder_representative(scc) + .and_then(|placeholder| placeholder.bound.kind.get_id()) + .and_then(|id| id.as_local()) + .map(|local| self.infcx.tcx.local_def_id_to_hir_id(local)) + else { + return; + }; // Look for the where-bound which introduces the placeholder. // As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>` // and `T: for<'a> Trait`<'a>. let mut hrtb_bounds = vec![]; - for pred in generics_impl.predicates { + // FIXME(amandasystems) we can probably flatten this. + for pred in self + .infcx + .tcx + .parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id)) + .generics() + .map(|gen_impl| gen_impl.predicates) + .into_iter() + .flatten() + { let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred.kind else { @@ -271,7 +284,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { Applicability::MaybeIncorrect, ); } - Some(()) } /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`. diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 6ffe439813ece..88f0f7f63d22e 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -230,7 +230,7 @@ pub(super) fn region_definitions<'tcx>( // Add external names from universal regions in fun function definitions. // FIXME: this two-step method is annoying, but I don't know how to avoid it. for (external_name, variable) in universal_regions.named_universal_regions_iter() { - debug!("region {:?} has external name {:?}", variable, external_name); + trace!("region {:?} has external name {:?}", variable, external_name); definitions[variable].external_name = Some(external_name); } (Frozen::freeze(definitions), has_placeholders) @@ -373,7 +373,7 @@ pub(crate) fn rewrite_placeholder_outlives<'tcx>( let Some(PlaceholderReachability { min_placeholder, max_placeholder, max_universe }) = annotation.reachable_placeholders else { - debug!("No placeholders reached from {scc:?}"); + trace!("No placeholders reached from {scc:?}"); continue; }; From 656caf6a51254308b2a31973ce70194634bb03cf Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 10 Nov 2025 21:47:08 +0100 Subject: [PATCH 15/17] One of the duplicate errors will not be flagged here --- .../higher-ranked-upcasting-ub.current.stderr | 12 +----------- .../trait-upcasting/higher-ranked-upcasting-ub.rs | 3 +-- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr index e5885ea35a71e..3f81b9306b9f1 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr @@ -7,16 +7,6 @@ LL | x = note: expected existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` found existential trait ref `for<'a> Supertrait<'a, 'a>` -error[E0308]: mismatched types - --> $DIR/higher-ranked-upcasting-ub.rs:22:5 - | -LL | x - | ^ one type is more general than the other - | - = note: expected existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` - found existential trait ref `for<'a> Supertrait<'a, 'a>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs index 98ca30ca391f7..203768b7a8792 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs @@ -21,8 +21,7 @@ impl<'a> Subtrait<'a, 'a> for () {} fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { x //[current]~^ ERROR mismatched types - //[current]~| ERROR mismatched types - //[next]~^^^ ERROR the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied + //[next]~^^ ERROR the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied } fn transmute<'a, 'b>(x: &'a str) -> &'b str { From 07c353be98da89305104147a629c4e0785921c1b Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Mon, 24 Nov 2025 14:40:39 +0100 Subject: [PATCH 16/17] Report fewer duplicate errors in tests --- .../associated-types-eq-hr.stderr | 57 +------------------ ...olved-typeck-results.no_assumptions.stderr | 14 +---- ...-ranked-auto-trait-1.no_assumptions.stderr | 20 +------ ...er-ranked-auto-trait-10.assumptions.stderr | 12 +--- ...ranked-auto-trait-10.no_assumptions.stderr | 12 +--- ...er-ranked-auto-trait-13.assumptions.stderr | 21 +------ ...ranked-auto-trait-13.no_assumptions.stderr | 40 +------------ ...ranked-auto-trait-15.no_assumptions.stderr | 12 +--- ...ranked-auto-trait-17.no_assumptions.stderr | 16 +----- ...-ranked-auto-trait-6.no_assumptions.stderr | 14 +---- .../issue-110963-early.no_assumptions.stderr | 17 +----- ...types-patterns-subtyping-ice-104779.stderr | 18 +----- .../extended/lending_iterator.stderr | 6 -- .../higher-ranked-lifetime-equality.stderr | 12 +--- ...ubtype.bound_inv_a_b_vs_bound_inv_a.stderr | 17 +----- .../trait-bounds/hrtb-conflate-regions.stderr | 12 +--- tests/ui/impl-trait/nested-rpit-hrtb.stderr | 8 ++- tests/ui/lub-glb/old-lub-glb-object.stderr | 12 +--- .../ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr | 12 +--- 19 files changed, 24 insertions(+), 308 deletions(-) diff --git a/tests/ui/associated-types/associated-types-eq-hr.stderr b/tests/ui/associated-types/associated-types-eq-hr.stderr index 3a70189dd9f94..68a2cbaac4756 100644 --- a/tests/ui/associated-types/associated-types-eq-hr.stderr +++ b/tests/ui/associated-types/associated-types-eq-hr.stderr @@ -61,26 +61,6 @@ LL | tuple_one::(); = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: implementation of `TheTrait` is not general enough - --> $DIR/associated-types-eq-hr.rs:96:5 - | -LL | tuple_one::(); - | ^^^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough - | - = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: implementation of `TheTrait` is not general enough - --> $DIR/associated-types-eq-hr.rs:96:5 - | -LL | tuple_one::(); - | ^^^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough - | - = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: implementation of `TheTrait` is not general enough --> $DIR/associated-types-eq-hr.rs:104:5 | @@ -90,16 +70,6 @@ LL | tuple_two::(); = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error: implementation of `TheTrait` is not general enough - --> $DIR/associated-types-eq-hr.rs:104:5 - | -LL | tuple_two::(); - | ^^^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough - | - = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0308]: mismatched types --> $DIR/associated-types-eq-hr.rs:104:5 | @@ -114,21 +84,6 @@ note: the lifetime requirement is introduced here LL | T: for<'x, 'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>, | ^^^^^^^^^^^^^ -error[E0308]: mismatched types - --> $DIR/associated-types-eq-hr.rs:104:5 - | -LL | tuple_two::(); - | ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected reference `&'x _` - found reference `&'y _` -note: the lifetime requirement is introduced here - --> $DIR/associated-types-eq-hr.rs:66:53 - | -LL | T: for<'x, 'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>, - | ^^^^^^^^^^^^^ - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: implementation of `TheTrait` is not general enough --> $DIR/associated-types-eq-hr.rs:116:5 | @@ -138,17 +93,7 @@ LL | tuple_four::(); = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error: implementation of `TheTrait` is not general enough - --> $DIR/associated-types-eq-hr.rs:116:5 - | -LL | tuple_four::(); - | ^^^^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough - | - = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 12 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0271, E0308. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr index d5560bf892032..72657c837b37a 100644 --- a/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr +++ b/tests/ui/async-await/drop-tracking-unresolved-typeck-results.no_assumptions.stderr @@ -9,17 +9,5 @@ LL | | }); = note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'0 ()>}` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`... = note: ...but it actually implements `FnOnce<(&(),)>` -error: implementation of `FnOnce` is not general enough - --> $DIR/drop-tracking-unresolved-typeck-results.rs:102:5 - | -LL | / send(async { -LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await -LL | | }); - | |______^ implementation of `FnOnce` is not general enough - | - = note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'0 ()>}` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `FnOnce<(&(),)>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr index b298a3bf2153a..e38735faab4b2 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-1.no_assumptions.stderr @@ -15,24 +15,6 @@ LL | | } = note: no two async blocks, even if identical, have the same type = help: consider pinning your async block and casting it to a trait object -error[E0308]: mismatched types - --> $DIR/higher-ranked-auto-trait-1.rs:37:5 - | -LL | / async { -LL | | let _y = &(); -LL | | let _x = filter(FilterMap { -LL | | f: || async move { *_y }, -... | -LL | | drop(_x); -LL | | } - | |_____^ one type is more general than the other - | - = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}` - found `async` block `{async block@$DIR/higher-ranked-auto-trait-1.rs:40:19: 40:29}` - = note: no two async blocks, even if identical, have the same type - = help: consider pinning your async block and casting it to a trait object - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr index 6fcf1b1eac176..b93b6f73f07c6 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-10.assumptions.stderr @@ -7,15 +7,5 @@ LL | Box::new(async move { get_foo(x).await }) = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`... = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2` -error: implementation of `Foo` is not general enough - --> $DIR/higher-ranked-auto-trait-10.rs:32:5 - | -LL | Box::new(async move { get_foo(x).await }) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough - | - = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`... - = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr index 6fcf1b1eac176..b93b6f73f07c6 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-10.no_assumptions.stderr @@ -7,15 +7,5 @@ LL | Box::new(async move { get_foo(x).await }) = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`... = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2` -error: implementation of `Foo` is not general enough - --> $DIR/higher-ranked-auto-trait-10.rs:32:5 - | -LL | Box::new(async move { get_foo(x).await }) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough - | - = note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`... - = note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr index 627049a2e2fa1..5a9ac7476f2a4 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.assumptions.stderr @@ -7,15 +7,6 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data)); = note: `Getter<'_>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'1>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` -error: implementation of `Getter` is not general enough - --> $DIR/higher-ranked-auto-trait-13.rs:65:5 - | -LL | assert_send(my_send_async_method(struct_with_lifetime, data)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough - | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... - = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` - error: implementation of `Getter` is not general enough --> $DIR/higher-ranked-auto-trait-13.rs:65:5 | @@ -26,15 +17,5 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data)); = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: implementation of `Getter` is not general enough - --> $DIR/higher-ranked-auto-trait-13.rs:65:5 - | -LL | assert_send(my_send_async_method(struct_with_lifetime, data)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough - | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... - = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr index c6c4c1c932c37..5a9ac7476f2a4 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-13.no_assumptions.stderr @@ -7,24 +7,6 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data)); = note: `Getter<'_>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'1>>`, for any two lifetimes `'0` and `'1`... = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` -error: implementation of `Getter` is not general enough - --> $DIR/higher-ranked-auto-trait-13.rs:65:5 - | -LL | assert_send(my_send_async_method(struct_with_lifetime, data)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough - | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... - = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` - -error: implementation of `Callable` is not general enough - --> $DIR/higher-ranked-auto-trait-13.rs:65:5 - | -LL | assert_send(my_send_async_method(struct_with_lifetime, data)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough - | - = note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`... - = note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1` - error: implementation of `Getter` is not general enough --> $DIR/higher-ranked-auto-trait-13.rs:65:5 | @@ -35,25 +17,5 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data)); = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: implementation of `Getter` is not general enough - --> $DIR/higher-ranked-auto-trait-13.rs:65:5 - | -LL | assert_send(my_send_async_method(struct_with_lifetime, data)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Getter` is not general enough - | - = note: `Getter<'1>` would have to be implemented for the type `GetterImpl<'0, ConstructableImpl<'_>>`, for any two lifetimes `'0` and `'1`... - = note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: implementation of `Callable` is not general enough - --> $DIR/higher-ranked-auto-trait-13.rs:65:5 - | -LL | assert_send(my_send_async_method(struct_with_lifetime, data)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough - | - = note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`... - = note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr index b4f3570d9f2da..fed8e92e59be8 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-15.no_assumptions.stderr @@ -7,15 +7,5 @@ LL | require_send(future); = note: closure with signature `fn(&'0 Vec) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec,)>`, for any two lifetimes `'0` and `'1`... = note: ...but it actually implements `FnOnce<(&Vec,)>` -error: implementation of `FnOnce` is not general enough - --> $DIR/higher-ranked-auto-trait-15.rs:20:5 - | -LL | require_send(future); - | ^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough - | - = note: closure with signature `fn(&'0 Vec) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec,)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `FnOnce<(&Vec,)>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr index 152900ca1ae9e..3f2278c714025 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-17.no_assumptions.stderr @@ -11,19 +11,5 @@ LL | | } = note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`... = note: ...but it actually implements `FnOnce<(&(),)>` -error: implementation of `FnOnce` is not general enough - --> $DIR/higher-ranked-auto-trait-17.rs:12:5 - | -LL | / async move { -LL | | let iter = Adaptor::new(a.iter().map(|_: &()| {})); -LL | | std::future::pending::<()>().await; -LL | | drop(iter); -LL | | } - | |_____^ implementation of `FnOnce` is not general enough - | - = note: closure with signature `fn(&'0 ())` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `FnOnce<(&(),)>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr index d1c88101618ae..813dc8ed26d0a 100644 --- a/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr +++ b/tests/ui/async-await/higher-ranked-auto-trait-6.no_assumptions.stderr @@ -9,18 +9,6 @@ LL | Box::new(async { new(|| async { f().await }).await }) = note: no two async blocks, even if identical, have the same type = help: consider pinning your async block and casting it to a trait object -error[E0308]: mismatched types - --> $DIR/higher-ranked-auto-trait-6.rs:16:5 - | -LL | Box::new(async { new(|| async { f().await }).await }) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}` - found `async` block `{async block@$DIR/higher-ranked-auto-trait-6.rs:16:29: 16:34}` - = note: no two async blocks, even if identical, have the same type - = help: consider pinning your async block and casting it to a trait object - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr b/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr index bb43636492409..1ce37253fd501 100644 --- a/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr +++ b/tests/ui/async-await/return-type-notation/issue-110963-early.no_assumptions.stderr @@ -12,20 +12,5 @@ LL | | }); = note: `Send` would have to be implemented for the type `impl Future { ::check<'0>(..) }`, for any two lifetimes `'0` and `'1`... = note: ...but `Send` is actually implemented for the type `impl Future { ::check<'2>(..) }`, for some specific lifetime `'2` -error: implementation of `Send` is not general enough - --> $DIR/issue-110963-early.rs:17:5 - | -LL | / spawn(async move { -LL | | let mut hc = hc; -LL | | if !hc.check().await { -LL | | log_health_check_failure().await; -LL | | } -LL | | }); - | |______^ implementation of `Send` is not general enough - | - = note: `Send` would have to be implemented for the type `impl Future { ::check<'0>(..) }`, for any two lifetimes `'0` and `'1`... - = note: ...but `Send` is actually implemented for the type `impl Future { ::check<'2>(..) }`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr index 887cb14a76935..33a18be5ff929 100644 --- a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr @@ -16,27 +16,11 @@ error: higher-ranked subtype error LL | match foo() { | ^^^^^ -error: higher-ranked subtype error - --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 - | -LL | match foo() { - | ^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: higher-ranked subtype error --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 | LL | Subtype::Bar => (), | ^^^^^^^^^^^^ -error: higher-ranked subtype error - --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 - | -LL | Subtype::Bar => (), - | ^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/generic-associated-types/extended/lending_iterator.stderr b/tests/ui/generic-associated-types/extended/lending_iterator.stderr index 7af95dc96a10a..84f5ed07bda59 100644 --- a/tests/ui/generic-associated-types/extended/lending_iterator.stderr +++ b/tests/ui/generic-associated-types/extended/lending_iterator.stderr @@ -12,12 +12,6 @@ error: `Self` does not live long enough | LL | >::from_iter(self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: due to a current limitation of the type system, this implies a `'static` lifetime - --> $DIR/lending_iterator.rs:4:21 - | -LL | fn from_iter LendingIterator = A>>(iter: T) -> Self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr index 8c0d66bb0bc1f..50b6d49ee4c0b 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr @@ -7,16 +7,6 @@ LL | let foo: Foo = foo; = note: expected struct `my_api::Foo fn(&'a (), &'b ())>` found struct `my_api::Foo fn(&'a (), &'a ())>` -error[E0308]: mismatched types - --> $DIR/higher-ranked-lifetime-equality.rs:34:25 - | -LL | let foo: Foo = foo; - | ^^^ one type is more general than the other - | - = note: expected struct `my_api::Foo fn(&'a (), &'b ())>` - found struct `my_api::Foo fn(&'a (), &'a ())>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr b/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr index 48703186cc634..10ad14967ae54 100644 --- a/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr +++ b/tests/ui/higher-ranked/subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr @@ -12,21 +12,6 @@ LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } found enum `Option fn(Inv<'a>, Inv<'a>)>` = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:54:13 - | -LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other -... -LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), -LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } - | |__________________________________- in this macro invocation - | - = note: expected enum `Option fn(Inv<'a>, Inv<'b>)>` - found enum `Option fn(Inv<'a>, Inv<'a>)>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-conflate-regions.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-conflate-regions.stderr index 69c58c5919e11..37f2128dd6424 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-conflate-regions.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-conflate-regions.stderr @@ -7,15 +7,5 @@ LL | fn b() { want_foo2::(); } = note: `SomeStruct` must implement `Foo<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... = note: ...but it actually implements `Foo<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error: implementation of `Foo` is not general enough - --> $DIR/hrtb-conflate-regions.rs:27:10 - | -LL | fn b() { want_foo2::(); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough - | - = note: `SomeStruct` must implement `Foo<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... - = note: ...but it actually implements `Foo<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.stderr b/tests/ui/impl-trait/nested-rpit-hrtb.stderr index a15f400fc2a76..8c7ab2e2a6e59 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.stderr +++ b/tests/ui/impl-trait/nested-rpit-hrtb.stderr @@ -91,6 +91,12 @@ LL | impl Qux<'_> for () {} | ^^^^^^^^^^^^^^^^^^^ = help: for that trait implementation, expected `()`, found `&'a ()` +error: lifetime may not live long enough + --> $DIR/nested-rpit-hrtb.rs:49:93 + | +LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} + | -- lifetime `'b` defined here ^^ opaque type requires that `'b` must outlive `'static` + error: implementation of `Bar` is not general enough --> $DIR/nested-rpit-hrtb.rs:49:93 | @@ -114,7 +120,7 @@ LL | impl Qux<'_> for () {} | ^^^^^^^^^^^^^^^^^^^ = help: for that trait implementation, expected `()`, found `&'a ()` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors Some errors have detailed explanations: E0261, E0277, E0657. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/lub-glb/old-lub-glb-object.stderr b/tests/ui/lub-glb/old-lub-glb-object.stderr index c476d6f692b7b..19cc1567770c5 100644 --- a/tests/ui/lub-glb/old-lub-glb-object.stderr +++ b/tests/ui/lub-glb/old-lub-glb-object.stderr @@ -7,16 +7,6 @@ LL | _ => y, = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>` found trait object `dyn for<'a> Foo<&'a u8, &'a u8>` -error[E0308]: mismatched types - --> $DIR/old-lub-glb-object.rs:9:14 - | -LL | _ => y, - | ^ one type is more general than the other - | - = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>` - found trait object `dyn for<'a> Foo<&'a u8, &'a u8>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr b/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr index 9a88ce8756c89..28113ac15db8c 100644 --- a/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr +++ b/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr @@ -7,16 +7,6 @@ LL | let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); = note: expected struct `Cell fn(&'a _, &'b _)>` found struct `Cell fn(&'a _, &'a _)>` -error[E0308]: mismatched types - --> $DIR/hr-fn-aau-eq-abu.rs:18:53 - | -LL | let a: Cell fn(&'a u32, &'b u32)> = make_cell_aa(); - | ^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected struct `Cell fn(&'a _, &'b _)>` - found struct `Cell fn(&'a _, &'a _)>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. From fbe2418c44e9c435941faa140ad6dc952f56095f Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Tue, 25 Nov 2025 13:24:41 +0100 Subject: [PATCH 17/17] Only annotate region graph SCCs with their representative for functions without placeholders --- .../rustc_borrowck/src/handle_placeholders.rs | 106 +++++++++++++----- .../rustc_borrowck/src/region_infer/mod.rs | 30 +++-- .../region_infer/opaque_types/region_ctxt.rs | 4 +- 3 files changed, 97 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 88f0f7f63d22e..4ebf969a794cd 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -24,12 +24,59 @@ use crate::type_check::{Locations, MirTypeckRegionConstraints}; use crate::universal_regions::UniversalRegions; use crate::{BorrowckInferCtxt, NllRegionVariableOrigin}; +pub(crate) trait RegionSccs { + fn representative(&self, scc: ConstraintSccIndex) -> RegionVid; + fn reachable_placeholders(&self, _scc: ConstraintSccIndex) -> Option { + None + } + /// The largest universe this SCC can name. It's the smallest + /// largest nameable universe of any reachable region, or + /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)` + fn max_nameable_universe(&self, _scc: ConstraintSccIndex) -> UniverseIndex { + UniverseIndex::ROOT + } + fn max_placeholder_universe_reached(&self, _scc: ConstraintSccIndex) -> UniverseIndex { + UniverseIndex::ROOT + } +} + +impl RegionSccs for IndexVec { + fn representative(&self, scc: ConstraintSccIndex) -> RegionVid { + self[scc].representative.rvid() + } + + fn reachable_placeholders(&self, scc: ConstraintSccIndex) -> Option { + self[scc].reachable_placeholders + } + + fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { + // Note that this is stricter than it might need to be! + self[scc].min_max_nameable_universe + } + + fn max_placeholder_universe_reached(&self, scc: ConstraintSccIndex) -> UniverseIndex { + self[scc].reachable_placeholders.map(|p| p.max_universe.u).unwrap_or(UniverseIndex::ROOT) + } +} + +impl RegionSccs for IndexVec { + fn representative(&self, scc: ConstraintSccIndex) -> RegionVid { + self[scc].rvid() + } +} + +impl FromRegionDefinition for Representative { + fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { + Representative::new(rvid, definition) + } +} + /// A set of outlives constraints after rewriting to remove /// higher-kinded constraints. pub(crate) struct LoweredConstraints<'tcx> { pub(crate) constraint_sccs: Sccs, pub(crate) definitions: Frozen>>, - pub(crate) scc_annotations: IndexVec, + pub(crate) scc_annotations: Box, pub(crate) outlives_constraints: Frozen>, pub(crate) type_tests: Vec>, pub(crate) liveness_constraints: LivenessValues, @@ -42,23 +89,28 @@ impl<'d, 'tcx, A: scc::Annotation> SccAnnotations<'d, 'tcx, A> { } } +trait FromRegionDefinition { + fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self; +} + /// A Visitor for SCC annotation construction. pub(crate) struct SccAnnotations<'d, 'tcx, A: scc::Annotation> { pub(crate) scc_to_annotation: IndexVec, definitions: &'d IndexVec>, } - -impl scc::Annotations for SccAnnotations<'_, '_, RegionTracker> { - fn new(&self, element: RegionVid) -> RegionTracker { - RegionTracker::new(element, &self.definitions[element]) +impl scc::Annotations + for SccAnnotations<'_, '_, A> +{ + fn new(&self, element: RegionVid) -> A { + A::new(element, &self.definitions[element]) } - fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: RegionTracker) { + fn annotate_scc(&mut self, scc: ConstraintSccIndex, annotation: A) { let idx = self.scc_to_annotation.push(annotation); assert!(idx == scc); } - type Ann = RegionTracker; + type Ann = A; type SccIdx = ConstraintSccIndex; } @@ -127,8 +179,8 @@ pub(crate) struct RegionTracker { pub(crate) representative: Representative, } -impl RegionTracker { - pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { +impl FromRegionDefinition for RegionTracker { + fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self { use NllRegionVariableOrigin::*; let min_max_nameable_universe = definition.universe; @@ -163,24 +215,12 @@ impl RegionTracker { }, } } - - /// The largest universe this SCC can name. It's the smallest - /// largest nameable universe of any reachable region, or - /// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)` - pub(crate) fn max_nameable_universe(self) -> UniverseIndex { - // Note that this is stricter than it might need to be! - self.min_max_nameable_universe - } - - pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex { - self.reachable_placeholders.map(|p| p.max_universe.u).unwrap_or(UniverseIndex::ROOT) - } } impl scc::Annotation for RegionTracker { fn update_scc(&mut self, other: &Self) { trace!("{:?} << {:?}", self.representative, other.representative); - self.representative = self.representative.min(other.representative); + self.representative.update_scc(&other.representative); self.is_placeholder = self.is_placeholder.max(other.is_placeholder); self.update_reachable(other); // SCC membership implies reachability. } @@ -291,19 +331,23 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( ) }; - let mut scc_annotations = SccAnnotations::init(&definitions); - let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); - - // This code structure is a bit convoluted because it allows for a planned - // future change where the early return here has a different type of annotation - // that does much less work. if !has_placeholders { debug!("No placeholder regions found; skipping rewriting logic!"); + // We skip the extra logic and only record representatives. + let mut scc_annotations: SccAnnotations<'_, '_, Representative> = + SccAnnotations::init(&definitions); + let constraint_sccs = ConstraintSccs::new_with_annotation( + &outlives_constraints + .graph(definitions.len()) + .region_graph(&outlives_constraints, fr_static), + &mut scc_annotations, + ); + return LoweredConstraints { type_tests, constraint_sccs, - scc_annotations: scc_annotations.scc_to_annotation, + scc_annotations: Box::new(scc_annotations.scc_to_annotation), definitions, outlives_constraints: Frozen::freeze(outlives_constraints), liveness_constraints, @@ -311,6 +355,8 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( }; } debug!("Placeholders present; activating placeholder handling logic!"); + let mut scc_annotations = SccAnnotations::init(&definitions); + let constraint_sccs = compute_sccs(&outlives_constraints, &mut scc_annotations); let added_constraints = rewrite_placeholder_outlives( &constraint_sccs, @@ -338,7 +384,7 @@ pub(crate) fn compute_sccs_applying_placeholder_outlives_constraints<'tcx>( LoweredConstraints { constraint_sccs, definitions, - scc_annotations, + scc_annotations: Box::new(scc_annotations), outlives_constraints: Frozen::freeze(outlives_constraints), type_tests, liveness_constraints, diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index ede1e69af9af6..d9c32641483e3 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use rustc_data_structures::frozen::Frozen; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::scc::{self, Sccs}; use rustc_errors::Diag; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_index::IndexVec; @@ -26,7 +26,7 @@ use crate::constraints::graph::NormalConstraintGraph; use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet}; use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; -use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; +use crate::handle_placeholders::{LoweredConstraints, RegionSccs, RegionTracker}; use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; @@ -74,6 +74,14 @@ impl Representative { } } +impl scc::Annotation for Representative { + fn update_scc(&mut self, other: &Self) { + *self = (*self).min(*other); + } + + fn update_reachable(&mut self, _other: &Self) {} +} + pub(crate) type ConstraintSccs = Sccs; pub struct RegionInferenceContext<'tcx> { @@ -102,7 +110,7 @@ pub struct RegionInferenceContext<'tcx> { /// compute the values of each region. constraint_sccs: ConstraintSccs, - scc_annotations: IndexVec, + scc_annotations: Box, /// Map universe indexes to information on why we created it. universe_causes: FxIndexMap>, @@ -499,9 +507,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_a: ConstraintSccIndex, scc_b: ConstraintSccIndex, ) -> bool { - self.scc_annotations[scc_a] - .max_nameable_universe() - .can_name(self.scc_annotations[scc_b].max_placeholder_universe_reached()) + self.scc_annotations + .max_nameable_universe(scc_a) + .can_name(self.scc_annotations.max_placeholder_universe_reached(scc_b)) } /// Once regions have been propagated, this method is used to see @@ -613,7 +621,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // It doesn't matter *what* universe because the promoted `T` will // always be in the root universe. - if !self.scc_annotations[r_scc].max_placeholder_universe_reached().is_root() { + if !self.scc_annotations.max_placeholder_universe_reached(r_scc).is_root() { debug!("encountered placeholder in higher universe, requiring 'static"); let static_r = self.universal_regions().fr_static; propagated_outlives_requirements.push(ClosureOutlivesRequirement { @@ -1091,7 +1099,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// The largest universe of any region nameable from this SCC. fn max_nameable_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex { - self.scc_annotations[scc].max_nameable_universe() + self.scc_annotations.max_nameable_universe(scc) } /// Checks the final value for the free region `fr` to see if it @@ -1452,7 +1460,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc: ConstraintSccIndex, ) -> Option { if let NllRegionVariableOrigin::Placeholder(p) = - self.definitions[self.scc_annotations[scc].representative.rvid()].origin + self.definitions[self.scc_annotations.representative(scc)].origin { Some(p) } else { @@ -1465,7 +1473,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, scc: ConstraintSccIndex, ) -> Option { - let ps = self.scc_annotations[scc].reachable_placeholders?; + let ps = self.scc_annotations.reachable_placeholders(scc)?; if let NllRegionVariableOrigin::Placeholder(p) = self.definitions[ps.min_placeholder].origin && ps.min_placeholder == ps.max_placeholder @@ -1775,7 +1783,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// they *must* be equal (though not having the same repr does not /// mean they are unequal). fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid { - self.scc_annotations[scc].representative.rvid() + self.scc_annotations.representative(scc) } pub(crate) fn liveness_constraints(&self) -> &LivenessValues { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs index 4cc11ba1e19cf..9fd2c45fcb072 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs @@ -9,7 +9,7 @@ use rustc_mir_dataflow::points::DenseLocationMap; use crate::BorrowckInferCtxt; use crate::constraints::ConstraintSccIndex; use crate::diagnostics::RegionErrors; -use crate::handle_placeholders::{SccAnnotations, region_definitions}; +use crate::handle_placeholders::{RegionSccs, SccAnnotations, region_definitions}; use crate::region_infer::reverse_sccs::ReverseSccGraph; use crate::region_infer::values::RegionValues; use crate::region_infer::{ @@ -110,7 +110,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { &self, scc: ConstraintSccIndex, ) -> UniverseIndex { - self.scc_annotations[scc].max_placeholder_universe_reached() + self.scc_annotations.max_placeholder_universe_reached(scc) } pub(super) fn universal_regions(&self) -> &UniversalRegions<'tcx> {