Skip to content

Clean up and speed up inference variable resolving code - #160913

Open
jdonszelmann wants to merge 8 commits into
rust-lang:mainfrom
jdonszelmann:cleanup-resolving
Open

Clean up and speed up inference variable resolving code#160913
jdonszelmann wants to merge 8 commits into
rust-lang:mainfrom
jdonszelmann:cleanup-resolving

Conversation

@jdonszelmann

@jdonszelmann jdonszelmann commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

View all comments

r? @lcnr

Clean up the resolve* family of functions in InferCtxt, renaming various functions to have more consistent and descriptive names. Adds a lot of documentation, and even wins some performance using the fact that shallow_resolve now returns root vids.

Reviewable commit by commit: some are large renames across the board, which are separated from the perf wins
and small renames in other commits to hopefully make more sense.

The functions are now called:

before after note
shallow_resolve_* (unchanged) shallow_resolve_* Now with newly added docs.
resolve_vars_if_possible_* deeply_resolve_ignoring_regions_* deliberately longer name, we want to encourage using deeply_resolve instead.
eager_resolve_vars deeply_resolve
fully_resolve (unchanged) fully_resolve Resolves with regions and asserts that all variables are resolved at the end.
opportunistic_resolve_var gone, same as shalllow_resolve now that we resolve to roots

Note

I've not used an LLM for any part of this PR, or any other PR I make. This includes any related work like research.

@rustbot

rustbot commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

changes to inspect_obligations.rs

cc @lcnr

changes to the core type system

cc @lcnr

Some changes occurred in need_type_info.rs

cc @lcnr

Some changes occurred to the core trait solver

cc @rust-lang/initiative-trait-system-refactor

changes to the core type system

cc @lcnr

@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. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Aug 11, 2026
@jdonszelmann

Copy link
Copy Markdown
Contributor Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 11, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 11, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

match self.inner.borrow_mut().type_variables().probe(vid) {
TypeVariableValue::Known { value } => Ok(value),
match value {
TypeVariableValue::Known { value } => Ok(self.shallow_resolve_non_recursive(value)),

@jdonszelmann jdonszelmann Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this method (and the one below) now also does the recursive resolving shallow_resolve already did. No tests change here.

View changes since the review

// Cold because the case in which a tyvar resolves to an intvar which resolves to a type is
// quite rare. It's way more common for `shallow_resolve_non_recursive` to return ty.
#[cold]
fn shallow_resolve_infer_non_recursive(&self, infer: InferTy, ty: Ty<'tcx>) -> Ty<'tcx> {

@jdonszelmann jdonszelmann Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the non-recursive caase helps ~0.5% on local benchmarks. Not much, but still a bit.

View changes since the review

}

ty::Infer(ty::IntVar(vid)) => {
let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid);

@jdonszelmann jdonszelmann Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

renames all the opportunistic_* functions with simply shallow_resolve_*. I've done this in many places. I've added a lot of docs to all the resolve methods, which I think makes it super clear that resolving is always an opportunistic process that doesn't necessarily resolve all variables, simply because it can't always.

From the perspective of a new contributor, they'll see a resolve_* function for the first time, go to its docs, and learn that the purpose of all resolve_* methods is to opportunistically resolve variables.

Since the behavior is the same for all the resolve_* functions I think that will actually make things clearer than randomly calling some of them "opportunistic" even when the others are inherently also opportunistic.

View changes since the review

/// In cases where we do, this can aid performance.
#[inline(always)]
fn shallow_resolve_ty_var(&self, v: TyVid, ty: Ty<'tcx>) -> Ty<'tcx> {
fn shallow_resolve_ty_var_with_ty(&self, v: TyVid, ty: Option<Ty<'tcx>>) -> Ty<'tcx> {

@jdonszelmann jdonszelmann Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

By taking an Option here, we can merge more methods' implementations. Doing this has 0 performance overhead, #[inline(always)] makes sure the callsites that always call with Some get optimized properly.

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could we instead change this function to not take a ty and return and Option instead?

@rust-bors rust-bors Bot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 11, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-bors

rust-bors Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

💔 Test for 7e8d4ec failed: CI. Failed job:

@rustbot rustbot added the T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. label Aug 11, 2026
@jdonszelmann

Copy link
Copy Markdown
Contributor Author

@bors try (failed due to missing rustdoc changes now applied)

@rust-bors

rust-bors Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Unknown argument "(failed". Did you mean to use @bors jobs=<jobs>|parent=<parent>? Run @bors help or go to https://bors.rust-lang.org/help to see available commands.

@rust-log-analyzer

This comment has been minimized.

@panstromek

Copy link
Copy Markdown
Contributor

@bors try

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 11, 2026
@rust-bors

rust-bors Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: d881e22 (d881e228b79422918a125ef8385170091369309b)
Base parent: a04c7a0 (a04c7a037d59b6dedc9f921e84e391f13a257aa4)

@rustbot

This comment has been minimized.

@jdonszelmann

Copy link
Copy Markdown
Contributor Author

@rustbot review

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 12, 2026
@lcnr lcnr changed the title Clean up and speed up resolving code Clean up and speed up inference variable resolving code Aug 12, 2026
Comment thread compiler/rustc_infer/src/infer/mod.rs Outdated
Comment thread compiler/rustc_infer/src/infer/mod.rs
Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid))
}
}
pub fn shallow_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> {

@lcnr lcnr Aug 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if we return None if the inner function doesn't make progress, could we always be explicit about reconstructing the type in the caller to make it explicit where we (likely unnecessarily) do so?

View changes since the review

.borrow_mut()
.unwrap_region_constraints()
.opportunistic_resolve_var(canonicalizer.tcx, vid);
.shallow_resolve_region_var(canonicalizer.tcx, vid);

@lcnr lcnr Aug 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we don't reuse r here if the root doesn't change 🤔 feels like doing so would be good for perf 🤔

View changes since the review

@lcnr

lcnr commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

so we have the following now

  • shallow_resolve_X now
  • deep_resolve_non_region_vars
  • eagerly_resolve_vars deep resolve which does resolve region vars
  • fully_resolve deep resolve which uses the region graph instead of only the unification tables

@rust-bors

This comment has been minimized.

@rustbot

This comment has been minimized.

@jdonszelmann

jdonszelmann commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

The functions are now called:

before after note
shallow_resolve_* (unchanged) shallow_resolve_* Now with newly added docs.
resolve_vars_if_possible_* deeply_resolve_ignoring_regions_* deliberately longer name, we want to encourage using deeply_resolve instead.
eager_resolve_vars deeply_resolve
fully_resolve (unchanged) fully_resolve Resolves with regions and asserts that all variables are resolved at the end.
opportunistic_resolve_var gone, same as shalllow_resolve now that we resolve to roots

@rust-bors

This comment has been minimized.

@rustbot

rustbot commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

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.

@lcnr lcnr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

reviewed most of the files.

can you split this PR into multiple separate ones. Reviewing that many changes in this PR feels kind of iffy

View changes since this review

Comment on lines +1669 to +1671
.filter(|t| {
!self.deeply_resolve_ignoring_regions_with_obligations(*t).is_ty_var()
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for separate PR: this call to deeply_resolve_ignoring_regions_with_obligations is redundant

.only_has_type(self)
.and_then(|ty| self.resolve_vars_with_obligations(ty).opt_tuple_fields())
.and_then(|ty| {
self.deeply_resolve_ignoring_regions_with_obligations(ty).opt_tuple_fields()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for separate PR: this call to deeply_resolve_ignoring_regions_with_obligations is redundant

return Err(TypeError::Mismatch);
}
Ok(self.resolve_vars_if_possible(adt_ty))
Ok(self.deeply_resolve_ignoring_regions(adt_ty))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for separate PR: this call to deeply_resolve_ignoring_regions_with_obligations is redundant

let mut pat_ref_layers = 0;
while let ty::Ref(_, inner_ty, mutbl) =
*self.resolve_vars_with_obligations(peeled_ty).kind()
*self.deeply_resolve_ignoring_regions_with_obligations(peeled_ty).kind()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for separate PR: this call is redundant

// region vids before processing regions, so we have a better chance to match clauses
// in our param-env.
let (sup_type, sub_region) = eager_resolve_vars(self, (sup_type, sub_region));
let (sup_type, sub_region) = deeply_resolve(self, (sup_type, sub_region));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you make this a method on the InferCtxt 🤔 we should push people to use that instead of deeply_resolve_ignoring_regions :>

&mut self,
tcx: TyCtxt<'tcx>,
vid: ty::RegionVid,
) -> ty::Region<'tcx> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

also separate: I feel like shallow_resolve_X should return Option and None if the vid doesn't change 🤔

there are a bunch of callers which shouldn't rebuild the ty/region here

Err(_) => Ty::new_var(self.tcx, self.root_var(vid)),
}
fn shallow_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> {
self.shallow_resolve_ty_var_with_ty(vid, None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dislike this function. Imo we should instead have shallow_resolve_ty_var_with_ty return Option<Ty> and then do the unwrap_or(ty) in the caller

/// Resolve a type variable to a type, if known.
/// Otherwise return a type with the root vid in it.
#[inline(always)]
fn shallow_resolve_ty_var_with_ty(&self, v: TyVid, ty: Option<Ty<'tcx>>) -> Ty<'tcx> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yeah, with_ty is :<

}

impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
impl<'a, 'tcx> DeepResolverIgnoringRegions<'a, 'tcx> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe just actually call that struct DeeplyResolveIgnoringRegions. DeepResolver sounds odd

@lcnr

lcnr commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

there are renames and behavior changes here. And I'd like to either first do behavior changes and then renames, or the other way round. but having something that has merge conflicts because it touches a bunch of files while also having subtle changes to the core of infer var handling is :/

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. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants