From 9f89751ce757eb17da891b958924a43174d6bd32 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 28 Aug 2026 13:22:55 +0000 Subject: [PATCH 1/3] Move polonius loan liveness computation prior to RegionInferenceContext::new --- compiler/rustc_borrowck/src/nll.rs | 23 ++++++++++++------- compiler/rustc_borrowck/src/polonius/mod.rs | 16 +++++++------ .../rustc_borrowck/src/region_infer/mod.rs | 7 ------ 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 672b58fcbfbea..f4a1edb0b675d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -126,7 +126,7 @@ 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 lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( + let mut lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( constraints, &universal_region_relations, infcx, @@ -144,6 +144,20 @@ pub(crate) fn compute_regions<'tcx>( &lowered_constraints, ); + // If requested for `-Zpolonius=next`, compute loan liveness information. + // This is done prior to `RegionInferenceContext::new`, because we may add + // additional liveness constraints. + if let Some(polonius_context) = polonius_context.as_mut() { + let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); + polonius_context.compute_loan_liveness( + &mut lowered_constraints.liveness_constraints, + lowered_constraints.outlives_constraints.outlives().iter().copied(), + &universal_region_relations.universal_regions, + body, + borrow_set, + ); + } + let mut regioncx = RegionInferenceContext::new( infcx, lowered_constraints, @@ -151,13 +165,6 @@ pub(crate) fn compute_regions<'tcx>( location_map, ); - // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints - // and use them to compute loan liveness. - if let Some(polonius_context) = polonius_context.as_mut() { - let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); - polonius_context.compute_loan_liveness(&mut regioncx, body, borrow_set) - } - // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 45108bfcb79ba..1c9242a3127a9 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -48,9 +48,11 @@ use rustc_mir_dataflow::points::PointIndex; pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; +use crate::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; use crate::region_infer::values::LivenessValues; -use crate::{BorrowSet, RegionInferenceContext}; +use crate::universal_regions::UniversalRegions; pub(crate) type LiveLoans = SparseBitMatrix; @@ -101,19 +103,19 @@ impl PoloniusContext { /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( &mut self, - regioncx: &mut RegionInferenceContext<'tcx>, + liveness: &mut LivenessValues, + outlives_constraints: impl Iterator>, + universal_regions: &UniversalRegions<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) { - let liveness = regioncx.liveness_constraints(); - // We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to // trace throughout localized constraints. if borrow_set.len() > 0 { // From the outlives constraints, liveness, and variances, we can compute reachability // on the lazy localized constraint graph to trace the liveness of loans, for the next // step in the chain (the NLL loan scope and active loans computations). - let graph = LocalizedConstraintGraph::new(liveness, regioncx.outlives_constraints()); + let graph = LocalizedConstraintGraph::new(liveness, outlives_constraints); let mut live_loans = LiveLoans::new(borrow_set.len()); let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans }; @@ -121,11 +123,11 @@ impl PoloniusContext { body, liveness, &self.live_region_variances, - regioncx.universal_regions(), + universal_regions, borrow_set, &mut visitor, ); - regioncx.record_live_loans(live_loans); + liveness.record_live_loans(live_loans); // The graph can be traversed again during MIR dumping, so we store it here. self.graph = Some(graph); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 19aa4081bfc88..d3fc7152acc44 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -30,7 +30,6 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; -use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; use crate::type_check::Locations; @@ -1874,12 +1873,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self.liveness_constraints } - /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active - /// loans dataflow computations. - pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { - self.liveness_constraints.record_live_loans(live_loans); - } - /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing /// region is contained within the type of a variable that is live at this point. /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. From 6ad5c1731c751fb25781cc3f7e74730b019544e9 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 28 Aug 2026 13:35:17 +0000 Subject: [PATCH 2/3] Move record_live_region_variance to be a freestanding function --- .../src/polonius/liveness_constraints.rs | 32 +++++++++---------- compiler/rustc_borrowck/src/polonius/mod.rs | 5 +-- .../src/type_check/liveness/mod.rs | 9 ++++-- .../src/type_check/liveness/trace.rs | 5 +-- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index b6f8b4a79f39b..4009a85180571 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -6,25 +6,23 @@ use rustc_middle::ty::relate::{ }; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; -use super::{ConstraintDirection, PoloniusContext}; +use super::ConstraintDirection; use crate::universal_regions::UniversalRegions; -impl PoloniusContext { - /// Record the variance of each region contained within the given value. - pub(crate) fn record_live_region_variance<'tcx>( - &mut self, - tcx: TyCtxt<'tcx>, - universal_regions: &UniversalRegions<'tcx>, - value: impl TypeVisitable> + Relate>, - ) { - let mut extractor = VarianceExtractor { - tcx, - ambient_variance: ty::Variance::Covariant, - directions: &mut self.live_region_variances, - universal_regions, - }; - extractor.relate(value, value).expect("Can't have a type error relating to itself"); - } +/// Record the variance of each region contained within the given value. +pub(crate) fn record_live_region_variance<'tcx>( + tcx: TyCtxt<'tcx>, + live_region_variances: &mut BTreeMap, + universal_regions: &UniversalRegions<'tcx>, + value: impl TypeVisitable> + Relate>, +) { + let mut extractor = VarianceExtractor { + tcx, + ambient_variance: ty::Variance::Covariant, + directions: live_region_variances, + universal_regions, + }; + extractor.relate(value, value).expect("Can't have a type error relating to itself"); } /// Extracts variances for regions contained within types. Follows the same structure as diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 1c9242a3127a9..cbac05d2eff67 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -48,6 +48,7 @@ use rustc_mir_dataflow::points::PointIndex; pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; +pub(crate) use self::liveness_constraints::record_live_region_variance; use crate::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; @@ -67,7 +68,7 @@ pub(crate) struct PoloniusContext { /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. - live_region_variances: BTreeMap, + pub(crate) live_region_variances: BTreeMap, /// The regions that outlive free regions are used to distinguish relevant live locals from /// boring locals. A boring local is one whose type contains only such regions. Polonius @@ -79,7 +80,7 @@ pub(crate) struct PoloniusContext { /// The direction a constraint can flow into. Used to create liveness constraints according to /// variance. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ConstraintDirection { +pub(crate) enum ConstraintDirection { /// For covariant cases, we add a forward edge `O at P1 -> O at P2`. Forward, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index fd8502773c51e..189a1634e56f8 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -11,7 +11,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusContext; +use crate::polonius::{PoloniusContext, record_live_region_variance}; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -220,7 +220,12 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { // When using `-Zpolonius=next`, we record the variance of each live region. if let Some(polonius_context) = self.polonius_context { - polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); + record_live_region_variance( + self.tcx, + &mut polonius_context.live_region_variances, + self.universal_regions, + value, + ); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index fe20bb6c28c0c..33e2da693ed1e 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; -use crate::polonius; +use crate::polonius::{self, record_live_region_variance}; use crate::region_infer::values; use crate::type_check::liveness::local_use_map::LocalUseMap; use crate::type_check::{NormalizeLocation, TypeChecker}; @@ -627,8 +627,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // When using `-Zpolonius=next`, we record the variance of each live region. if let Some(polonius_context) = typeck.polonius_context.as_mut() { - polonius_context.record_live_region_variance( + record_live_region_variance( typeck.infcx.tcx, + &mut polonius_context.live_region_variances, typeck.universal_regions, value, ); From b58ee5bb9a6171b5b4519a237f0bdcef2a5e7eb2 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 28 Aug 2026 14:50:00 +0000 Subject: [PATCH 3/3] Minor trace updates --- .../src/type_check/liveness/mod.rs | 2 +- .../src/type_check/liveness/trace.rs | 80 ++++++++----------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 189a1634e56f8..dfab2fd071773 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -67,7 +67,7 @@ pub(super) fn generate<'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals); + trace::trace(typeck, location_map, move_data, &relevant_live_locals, &boring_locals); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 33e2da693ed1e..89a8899a991c9 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -3,7 +3,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; -use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location}; +use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; @@ -19,6 +19,7 @@ use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; +use crate::BorrowckInferCtxt; use crate::polonius::{self, record_live_region_variance}; use crate::region_infer::values; use crate::type_check::liveness::local_use_map::LocalUseMap; @@ -42,8 +43,8 @@ pub(super) fn trace<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, location_map: &DenseLocationMap, move_data: &MoveData<'tcx>, - relevant_live_locals: Vec, - boring_locals: Vec, + relevant_live_locals: &[Local], + boring_locals: &[Local], ) { let _timer = typeck.tcx().prof.generic_activity("borrowck_liveness_trace"); @@ -59,7 +60,7 @@ pub(super) fn trace<'tcx>( let mut results = LivenessResults::new(cx); - results.add_extra_drop_facts(&relevant_live_locals); + results.add_extra_drop_facts(relevant_live_locals); results.compute_for_all_locals(relevant_live_locals); @@ -131,8 +132,8 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } } - fn compute_for_all_locals(&mut self, relevant_live_locals: Vec) { - for local in relevant_live_locals { + fn compute_for_all_locals(&mut self, relevant_live_locals: &[Local]) { + for &local in relevant_live_locals { self.reset_local_state(); self.add_defs_for(local); self.compute_use_live_points_for(local); @@ -161,20 +162,11 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { /// These are all the locals which do not potentially reference a region local /// to this body. Locals which only reference free regions are always drop-live /// and can therefore safely be dropped. - fn dropck_boring_locals(&mut self, boring_locals: Vec) { - for local in boring_locals { + fn dropck_boring_locals(&mut self, boring_locals: &[Local]) { + for &local in boring_locals { let local_ty = self.cx.body().local_decls[local].ty; let local_span = self.cx.body().local_decls[local].source_info.span; - let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ - let typeck = &self.cx.typeck; - move || LivenessContext::compute_drop_data(typeck, local_ty, local_span) - }); - - drop_data.dropck_result.report_overflows( - self.cx.typeck.infcx.tcx, - self.cx.typeck.body.local_decls[local].source_info.span, - local_ty, - ); + dropck_local(&self.cx.typeck.infcx, &mut self.cx.drop_data, local_ty, local_span); } } @@ -567,11 +559,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { values::pretty_print_points(self.location_map, live_at.iter()), ); - let local_span = self.body().local_decls()[dropped_local].source_info.span; - let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ - let typeck = &self.typeck; - move || Self::compute_drop_data(typeck, dropped_ty, local_span) - }); + let dropped_span = self.body().local_decls[dropped_local].source_info.span; + let drop_data = + dropck_local(&self.typeck.infcx, &mut self.drop_data, dropped_ty, dropped_span); if let Some(data) = &drop_data.region_constraint_data { for &drop_location in drop_locations { @@ -583,12 +573,6 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } - drop_data.dropck_result.report_overflows( - self.typeck.infcx.tcx, - self.typeck.body.source_info(*drop_locations.first().unwrap()).span, - dropped_ty, - ); - // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { @@ -635,17 +619,19 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { ); } } +} - fn compute_drop_data( - typeck: &TypeChecker<'_, 'tcx>, - dropped_ty: Ty<'tcx>, - span: Span, - ) -> DropData<'tcx> { - debug!("compute_drop_data(dropped_ty={:?})", dropped_ty); - - let goal = DropckOutlives { dropped_ty }; - - match typeck.infcx.fully_perform(goal, DUMMY_SP) { +/// Computes the `DropData` for a given type, caching the result. +/// This also reports the overflow errors from the computation, if any. +fn dropck_local<'tcx, 'd>( + infcx: &BorrowckInferCtxt<'tcx>, + drop_data: &'d mut FxIndexMap, DropData<'tcx>>, + local_ty: Ty<'tcx>, + local_span: Span, +) -> &'d DropData<'tcx> { + let compute_drop_data = || { + let goal = DropckOutlives { dropped_ty: local_ty }; + match infcx.fully_perform(goal, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } @@ -657,12 +643,12 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // // Do this inside of a probe because we don't particularly care (or want) // any region side-effects of this operation in our infcx. - typeck.infcx.probe(|_| { - let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx); + infcx.probe(|_| { + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let errors = match dropck_outlives::compute_dropck_outlives_with_errors( &ocx, - typeck.infcx.param_env.and(goal), - span, + infcx.param_env.and(goal), + local_span, ) { Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => TraitErrors::HasErrors(e), @@ -671,11 +657,15 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // Could have no errors if a type lowering error, say, caused the query // to fail. if let TraitErrors::HasErrors(errors) = errors { - typeck.infcx.err_ctxt().report_fulfillment_errors(errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } }); DropData { dropck_result: Default::default(), region_constraint_data: None } } } - } + }; + + let drop_data = drop_data.entry(local_ty).or_insert_with(compute_drop_data); + drop_data.dropck_result.report_overflows(infcx.tcx, local_span, local_ty); + drop_data }