Skip to content

Conversation

@amandasystems
Copy link
Contributor

@amandasystems amandasystems commented Jan 26, 2026

What?

This PR turns RegionInferenceContext into a builder that produces the inferred region values, InferredRegions. The resulting struct implements the public API of RegionInferenceContext and replaces it in consumers.rs. RegionInferenceContext::solve() now consumes (moves) the inference context. It is completely private to region inference.

Why?

  • RegionInferenceContext has become a huge dump for various values people want to access.
  • region_infer itself is a very large file that's difficult to find your way around.
  • Polonius wants to skip as much of region inference as possible, since most of it is redundant. This makes that possible
  • No method now need warnings about being called after region inference or else they'll panic
  • RegionInferenceContext now takes almost all of its fields by reference
  • We can probably drop at least some stuff earlier
  • Data dependencies during region inference are now made more explicit

Knock-on effects

  • Region constraint (blame) search is now independent of a RegionInferenceContext
  • Less data is retained after region inference. This may or may not lead to minor improvements in memory use.
  • RegionInferenceContext now almost exclusively contains references to values, as opposed to owning them. This addresses most of fn compute_closure_requirements_modulo_opaques shouldn't clone all its inputs #146079
  • This reduces the coupling between region inference and Polonius-next, making it possible to skip region inference if it is not needed to check universal regions or type tests.
  • region_infer gains two child modules and becomes a lot less of a behemoth.

Detailed overview of changes

  • new modules: region_infer::constraint_search and region_infer::universal_regions.
  • handle_placeholders now consumes less input and does constraint rewriting via mutable references
  • consumers.rs now has a InferenceResults instead of a RegionInferenceContext
  • The public methods eval_{equal, outlives} are moved to InferredRegions
  • Constraint (blame) search, MIR dumping, graphviz rendering, and checking universal regions now all get their own builder-style structs, and the methods for their respective tasks are moved onto them
  • The option for region inference results in ConsumerOptions is now called InferredRegions
  • calculate_borrows_out_of_scope_at_location of course now takes InferredRegions
  • LivenessValues for free regions is now initialised as live at all points along with the constraint rewriting during placeholder handling, as opposed to during construction of RegionInferenceContext
  • Some trace logging, especially during constraint search, is lost since the data required to print the logs was only necessary to print the logs and never used otherwise.

r? @lcnr

Closes #146079.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 26, 2026
@rust-log-analyzer

This comment has been minimized.

@amandasystems amandasystems force-pushed the split-region-inference branch from 6befd69 to 13a57f6 Compare January 26, 2026 14:50
@rust-log-analyzer

This comment has been minimized.

@amandasystems amandasystems force-pushed the split-region-inference branch from 13a57f6 to 00e8121 Compare January 26, 2026 16:12
@amandasystems amandasystems force-pushed the split-region-inference branch from 00e8121 to 454f98a Compare January 28, 2026 22:21
@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

This ensures all of region inference is immutable, and makes every
operation that requires region inference to have been executed to run
explicitly require the results.
Since this collapses `RegionInferenceContext`, `::solve()` now
consumes the inference context.
@amandasystems amandasystems force-pushed the split-region-inference branch from 4c01f95 to 174023c Compare February 6, 2026 14:58
@rustbot
Copy link
Collaborator

rustbot commented Feb 6, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

@rust-bors

This comment has been minimized.

Comment on lines +721 to +729
skip(
infcx,
body,
polonius_output,
location_map,
placeholder_indices,
constraint_sccs,
liveness_constraints
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip_all + fields?

let num_external_vids =
universal_region_relations.universal_regions.num_global_and_external_regions();

let regioncx = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why a block in the let?

start_point: Location,
) -> Option<Cause> {
let mut uf = UseFinder { body, regioncx, tcx, region_vid, start_point };
let mut uf = UseFinder { body, tcx, region_vid, start_point, scc_values: &scc_values };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the explicit & here

Comment on lines 211 to +212
borrow_region: ty::RegionVid,
universal_regions: &'a UniversalRegions<'tcx>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swap these two fields

);

assert!(self.regioncx.universal_regions().is_universal_region(fr));
//FIXME! assert!(self.regioncx.universal_regions().is_universal_region(fr));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

let (_, span) = self.regioncx.get_argument_name_and_span_for_region(
let arg_ty =
self.universal_regions().unnormalized_input_tys[implicit_inputs + argument_index];
let (_, span) = self.universal_regions().get_argument_name_and_span_for_region(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move self.universal_regions() to a let stmt?

let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region(
let upvar_index =
self.universal_regions().get_upvar_index_for_region(self.infcx.tcx, fr)?;
let (upvar_name, upvar_span) = self.universal_regions().get_upvar_name_and_span_for_region(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well

liveness: &LivenessValues,
pass_where: PassWhere,
out: &mut dyn io::Write,
) -> io::Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls use a consistent function param ordering between the functions here :<

let (definitions, _has_placeholders) = region_definitions(
infcx,
universal_regions,
&mut constraints.liveness_constraints.clone(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is... very odd. You give a mutable reference to a clone?

please move the cloned thing into a let binding first

outlives_constraints: &'a OutlivesConstraintSet<'tcx>,
mut w: &mut dyn Write,
) -> io::Result<()> {
dot::render(&RawConstraints { tcx, region_definitions, outlives_constraints }, &mut w)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why reference RawConstraints. That type should be Copy

dot::render(&SccConstraints { tcx, region_definitions, constraint_sccs, nodes_per_scc }, &mut w)
}

struct RawConstraints<'a, 'tcx> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this copy and take it by value

nodes_per_scc: IndexVec<ConstraintSccIndex, Vec<RegionVid>>,
region_definitions: &'a IndexVec<RegionVid, RegionDefinition<'tcx>>,
constraint_sccs: &'a ConstraintSccs,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

outlives_requirements.as_mut(),
&mut errors_buffer,
type_tests,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I would prefer check_type_tests to stay on the region inference context 🤔

they are a part of region inference to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or like, the RegionInferenceCtxt should either not exist at all anymore, or still be used for the region computations 🤔 maybe chat about it in sync

if let NllRegionVariableOrigin::FreeRegion = origin {
// Add all nodes in the CFG to liveness constraints for free regions
liveness_constraints.add_all_points(rvid);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a functional change? 🤔

Copy link
Contributor

@lcnr lcnr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scc_values: InferredRegions feels wrong. Maybe just computed_regions: ComputedRegions, regions: InferredRegions

region_definitions why mutable liveness constraints

MirDumper::dump_mir why is scc_values an fn param instead of a field?

View changes since this review

Comment on lines +102 to +103
// These clones can more or less be avoided,
// since they are probably idempotent.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what exactly does this comment mean? It's confusing to me

@lcnr
Copy link
Contributor

lcnr commented Feb 12, 2026

Can you rebase, I will then do a perf run and locally look at the docs after this PR as that should make it clearer how the API has changed

placeholder_indices: PlaceholderIndices<'tcx>,
) -> (Option<ClosureRegionRequirements<'tcx>>, RegionErrors<'tcx>, InferredRegions<'tcx>) {
let num_external_vids =
universal_region_relations.universal_regions.num_global_and_external_regions();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did u move this one up?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fn compute_closure_requirements_modulo_opaques shouldn't clone all its inputs

4 participants