Skip to content

move implied bounds computation out of borrowck - #160491

Open
lcnr wants to merge 6 commits into
rust-lang:mainfrom
lcnr:implied-bounds-opaque
Open

move implied bounds computation out of borrowck#160491
lcnr wants to merge 6 commits into
rust-lang:mainfrom
lcnr:implied-bounds-opaque

Conversation

@lcnr

@lcnr lcnr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

First 3 commits don't change behavior and I moved them into #160504.


@tiif did the initial implementation work in #152051. This ended up being more involved than I originally expected, so I ended up finishing this PR after spending a few days on it myself.

Computing implied bounds now happens in a new query mir_borrowck_implied_outlives_bounds which does two things differently from MIR borrowck:

  • we use param and placeholder regions instead of NLL vars
  • if we're a typeck root we don't reveal any opaque types

Using param and placeholder regions instead of NLL vars

This fixes #106569. We previously computed the implied bounds using ty::ReVar even for universal variables, which meant that resolving them can drop constraints in borrowck.

As explained in #106569 (comment)

what's happening here is that normalizing <Equal<'a, 'b> as Trait>::Ty requires 'a and 'b to be equal. We represent 'a and 'b as region inference variables right now.

This means the 'a == 'b constraint does not get returned as region outlives constraints, but we instead just unify the two existentials. The implied bound 'a == 'b then ends up as 'a == 'a which means in the caller, we can't reconstruct that it's actually about 'a and 'b.

Computing implied bounds now uses universal variables instead of ReVar, fixing this issue.

Do not reveal the hidden type of opaques for typeck roots

This fixes rust-lang/trait-system-refactor-initiative#159 with the new trait solver.

trait Extend<'a, 'b> {
    fn extend(self, _: &'a str) -> &'b str;
}
impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> {
    fn extend(self, s: &'a str) -> &'b str {
        s
    }
}

fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
    None::<&'b &'a ()>
}

fn main() {
    let y = boom().extend(&String::from("temporary"));
    println!("{}", y);
}

Computing the implied bounds for boom previously revealed the hidden type of impl Extend<'a, 'b> giving us a 'a: 'b implied bound. Calling boom cannot reveal the opaque type as it's outside of the defining scope, so the caller never has to prove that outlives requirement.

We do still reveal opaque types when computing the implied bounds for nested bodies! This is subtle and I nearly missed this. For nested bodies, they are only ever used inside of their parent function, which is able to define the same opaque types. We never check that e.g. a closure is well-formed outside of the parent body.

This means trying to compute implied bounds for closures without defining opaque types can result in incorrect errors, see tests/ui/traits/next-solver/opaques/implied-bounds-opaque-hidden-in-closure-sig.rs:

trait Trait {
    type Assoc;
}
impl Trait for () {
    type Assoc = ();
}

trait Func {
    type Output;
}
impl<F: FnOnce() -> R, R> Func for F {
    type Output = R;
}

struct RequiresWf<F>(F)
where
    F: Func,
    F::Output: Trait,
    <F::Output as Trait>::Assoc: 'static;

fn opaque() -> impl Sized {
    // Closure signature is `fn(RequiresWf<fn_def>)` and we need to
    // normalize the RPIT for the where-clauses of the argument to hold.
    (|_| ())(RequiresWf(opaque));
}

Implementation details and nuances

var_values

Returning implied bounds and canonicalization. Figuring out how to do so was quite challenging. The main question is how to link regions from the query to the correct regions in MIR borrowck. The way to do so is via var_values.

As we're using old style canonicalizing we keep early and late bound parameters around, so these don't have to be part of the var_values. We do need to link regions from the closure signature in the query to the regions in the signature used in MIR borrowck. We do this by going over the signature and collecting all regions we find in the var_values. The query uses placeholders for these while MIR borrowck uses external NLL vars for them.

Normalizing the signature and unconstrained region vars

Normalizing a function signature can result in unconstrained existential regions due to #136547. Types involving these regions can be relevant for implied bounds. Using such type outlives bounds relies on structural equality. If we separately normalize the signature two times, once in borrowck and once in the implied bounds query, we get different unconstrained region vars, breaking the gluon_salsa test.

To avoid this, mir_borrowck_implied_outlives_bounds normalizes the signature without revealing opaque types and returns its result to MIR borrowck. MIR borrowck now renormalizes this signature to also correctly normalize opaque types.

The bevy implied bounds hack

This PR keeps the current behavior of #119956 while somewhat changing the actual implementation.

We continue to consider constraints from computing implied bounds as implied bounds only for arguments whose type mentions bevy_ecs::ParamSet.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. 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 4, 2026
@lcnr

lcnr commented Aug 4, 2026

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 4, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 4, 2026
move implied bounds computation out of borrowck
@lcnr
lcnr force-pushed the implied-bounds-opaque branch 2 times, most recently from 48a8766 to 5c0c099 Compare August 4, 2026 09:44
@lcnr
lcnr force-pushed the implied-bounds-opaque branch 3 times, most recently from 6eb8247 to 1dd6c27 Compare August 4, 2026 10:01
@lcnr

lcnr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@bors cancel

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

rust-bors Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

❗ There is currently no auto build in progress on this PR.

Hint: There is a pending try build on this PR. Maybe you meant to cancel it? You can do that using @bors try cancel.

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 4, 2026
move implied bounds computation out of borrowck
@lcnr

lcnr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 4, 2026
move implied bounds computation out of borrowck
@lcnr
lcnr marked this pull request as ready for review August 4, 2026 12:17
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 4, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Aug 4, 2026
@rustbot

rustbot commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

r? @JohnTitor

rustbot has assigned @JohnTitor.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 75 candidates
  • Random selection from 20 candidates

@lcnr

lcnr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

r? @adwinwhite @BoxyUwU

@rustbot rustbot assigned adwinwhite and unassigned JohnTitor Aug 4, 2026
@lcnr
lcnr force-pushed the implied-bounds-opaque branch from 4b5fc7f to 70041ae Compare August 4, 2026 12:22
//@ [wfcheck] check-pass
//@ [borrowck_current] check-fail
//@ [borrowck_current] known-bug: #106569
//@ [borrowck_next] compile-flags: -Znext-solver

@Randl Randl Aug 4, 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.

Don't we want to keep this?

View changes since the review

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.

what do you mean? we are keeping this test. The only thing that changed is that this test now passes with all revisions

woops. jup jup

you're totally right 🤣 thank you

@rust-bors

rust-bors Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 615014f (615014f15745532730b9ce7a1d9309f8b4756f93)
Base parent: c9ff496 (c9ff496891c278ad660bc0ab85c1f0b72059464a)

@rust-timer

This comment has been minimized.

@lcnr
lcnr force-pushed the implied-bounds-opaque branch from 70041ae to 9151a02 Compare August 4, 2026 13:52
@lcnr

lcnr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@craterbot check

@craterbot

Copy link
Copy Markdown
Collaborator

👌 Experiment pr-160491 created and queued.
🤖 Automatically detected try build 615014f
⚠️ Try build based on commit 4b5fc7f, but latest commit is 9151a02. Did you forget to make a new try build?
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Aug 4, 2026
@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (615014f): comparison URL.

Overall result: ❌✅ regressions and improvements - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never rustc-perf
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.4% [0.2%, 0.9%] 40
Regressions ❌
(secondary)
0.7% [0.1%, 1.4%] 27
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.5% [-0.9%, -0.2%] 12
All ❌✅ (primary) 0.4% [0.2%, 0.9%] 40

Max RSS (memory usage)

Results (primary 1.3%, secondary -0.5%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.0% [0.5%, 4.6%] 3
Regressions ❌
(secondary)
0.6% [0.6%, 0.6%] 1
Improvements ✅
(primary)
-0.7% [-0.7%, -0.7%] 1
Improvements ✅
(secondary)
-0.6% [-1.1%, -0.4%] 16
All ❌✅ (primary) 1.3% [-0.7%, 4.6%] 4

Cycles

Results (primary -0.1%, secondary -0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
1.0% [0.5%, 1.4%] 11
Regressions ❌
(secondary)
1.1% [0.4%, 2.3%] 8
Improvements ✅
(primary)
-2.6% [-7.2%, -0.6%] 5
Improvements ✅
(secondary)
-1.6% [-3.2%, -0.5%] 6
All ❌✅ (primary) -0.1% [-7.2%, 1.4%] 16

Binary size

This perf run didn't have relevant results for this metric.

Bootstrap: 489.838s -> 489.061s (-0.16%)
Artifact size: 390.28 MiB -> 390.68 MiB (0.10%)

@rustbot rustbot added the perf-regression Performance regression. label Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf-regression Performance regression. S-waiting-on-crater Status: Waiting on a crater run to be completed. 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)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

normalizing opaques while computing implied bounds implied bounds: lifetime equality lost after normalization

8 participants