Skip to content

Skip redundant next-solver fulfillment scans - #1

Draft
anonrig wants to merge 2 commits into
mainfrom
cursor/rustc-faster-778d
Draft

Skip redundant next-solver fulfillment scans#1
anonrig wants to merge 2 commits into
mainfrom
cursor/rustc-faster-778d

Conversation

@anonrig

@anonrig anonrig commented Aug 21, 2026

Copy link
Copy Markdown
Owner

This change was implemented by a Cursor cloud agent. It is intended for this fork, not as an upstream rust-lang/rust PR.

Skip walking the next-solver pending fulfillment queue when no pending goal can have been unstalled.

Refs rust-lang#159933. Related idea: rust-lang#161348, but this keeps a simpler conservative check:

  • Track the smallest type vid equated, sub-unified, or instantiated since the last fulfillment pass (snapshot-aware via the existing infer undo log).
  • Track the largest stalled type vid / sub-root among pending goals.
  • Skip the retain_mut scan when every pending goal is type-var-only and trackable, opaque storage is unchanged, and every infer change is to a strictly newer vid.
  • Int/float/const stalls, mixed opaque counts, and -Zdisable-fast-paths take the existing path.

Missing a min_changed bump is conservative (extra scans). Missing a bump on equate / instantiate / sub_unify would be incorrect; those three paths all record the change.

Measurements

Same-commit stage1 rustc, --crate-type=lib --edition=2024 --emit=metadata. Median of 4 runs after warmup.

Workload Before After Change
rust-lang#159933 N=2000 Vec::push(Default::default()) 3.484s 1.078s 69.1% faster
rust-lang#159933 N=4000 13.459s 4.061s 69.8% faster
400 independent Default/unwrap_or_default bindings 0.055s 0.054s ~1.7% (noise)

The 10%+ win is on next-solver typeck of large function bodies with many stalled obligations, which is the default solver on this tree.


Note

Medium Risk
Touches next-solver fulfillment and type-inference undo logging. A missed min_changed bump on equate/instantiate/sub_unify could skip needed re-evaluation; the skip is otherwise conservative.

Overview
Next-solver fulfillment can now skip walking the pending obligation queue when no pending goal could have been unstalled (rustc#159933).

It tracks the smallest type vid equated, sub-unified, or instantiated since the last fulfillment pass (snapshot-aware via the infer undo log) and the largest stalled type vid among pending goals. The scan is skipped only when every pending goal is type-var-only and trackable, opaque storage is unchanged, and every infer change is to a strictly newer vid. Int/float/const stalls, mixed opaque counts, overflow, and -Zdisable-fast-paths keep the existing full walk.

A UI test covers a long chain of stalled Default obligations that must still resolve when a later u8 constraint appears.

Reviewed by Cursor Bugbot for commit 18ea519. Bugbot is set up for automated code reviews on this repo. Configure here.

When every pending goal is stalled only on type inference variables
older than any vid changed since the last fulfillment pass, skip
walking the pending queue. This removes the quadratic rescan from
large typeck bodies (rustc#159933) without tracking a set of vids.

Int/float/const stalls and opaque-storage count mismatches still
take the existing retain_mut path. -Zdisable-fast-paths disables
the skip.
typos already allowlists 'unstalled'; the verb form trips CI.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 18ea519. Configure here.

}

infcx.reset_min_changed_ty_vid();
self.recompute_tracking();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shared min-changed reset breaks skip

High Severity

reset_min_changed_ty_vid clears InferCtxt-global change tracking after any fulfillment scan. Temporary ObligationCtxt / FulfillmentCtxt instances used during typeck (e.g. coercion) share that state with the main fulfillment engine, so a successful nested scan can wipe a relevant min_changed and cause the outer context to take the skip path even though pending goals were unstalled. Those goals are later reported as ambiguity instead of being re-solved or hard-errored.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 18ea519. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants