Skip to content

Defer closure method receivers to be inferred from body - #159829

Open
chenyukang wants to merge 9 commits into
rust-lang:mainfrom
chenyukang:yukang-fix-76112-closure-index-inference
Open

Defer closure method receivers to be inferred from body#159829
chenyukang wants to merge 9 commits into
rust-lang:mainfrom
chenyukang:yukang-fix-76112-closure-index-inference

Conversation

@chenyukang

@chenyukang chenyukang commented Jul 24, 2026

Copy link
Copy Markdown
Member

Fixes #76112

Defer method confirmation for unresolved intermediate receiver types until the enclosing body has added its inference constraints.

r? @ghost

@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. labels Jul 24, 2026
@chenyukang

Copy link
Copy Markdown
Member Author

Let's do a perf run:

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

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Jul 24, 2026
…erence, r=<try>

Defer closure method receivers to be inferred from body
@rust-bors

rust-bors Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: e17ab16 (e17ab16794c7c28ddc10a41e154377c42713fa2d)
Base parent: 29e68fe (29e68fe2295f8fc2feb52b8cb0b61a055842fdcf)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (e17ab16): comparison URL.

Overall result: no relevant changes - no action needed

Benchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up.

@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

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

Max RSS (memory usage)

Results (secondary 0.8%)

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

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
6.4% [6.4%, 6.4%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-4.8% [-4.8%, -4.8%] 1
All ❌✅ (primary) - - 0

Cycles

Results (secondary 3.5%)

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

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
3.5% [3.5%, 3.6%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

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

Bootstrap: 488.104s -> 486.507s (-0.33%)
Artifact size: 387.69 MiB -> 387.70 MiB (0.00%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Jul 24, 2026
@chenyukang
chenyukang force-pushed the yukang-fix-76112-closure-index-inference branch from 053cf2e to 8dd1996 Compare July 25, 2026 01:58
@chenyukang
chenyukang marked this pull request as ready for review July 25, 2026 02:06
@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 Jul 25, 2026
@chenyukang

Copy link
Copy Markdown
Member Author

r? types

@rustbot rustbot added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Jul 25, 2026
@chenyukang
chenyukang marked this pull request as draft July 25, 2026 14:33
@rustbot rustbot 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 Jul 25, 2026
@chenyukang

Copy link
Copy Markdown
Member Author

I used this issue as a chance to learn more about how type checking and type inference work in rustc.

I experimented with deferring method-call resolution until later constraints from the closure become available.

Then I found two consequences:

  1. Later closure invocations can affect which diagnostic is emitted. For example, this test reports E0282 on main, but E0308 with this PR. This may be a more useful diagnostic, but it is still a behavior difference that we should consider. See this commit.
  2. nested closure will require fixed-point processing, see commit de921d4

so I don't think my current approach is ideal: it introduces additional deferred state, make the code more complicated becaused of a relatively narrow inference case.

I would appreciate feedback from reviewers who are more familiar with this part.

@chenyukang
chenyukang marked this pull request as ready for review July 25, 2026 15:26
@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 Jul 25, 2026
@oli-obk oli-obk added needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 29, 2026
@oli-obk

oli-obk commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Hmm... there are various similar issues with type inference within closures when the closure doesn't have an expectation (e.g. by being inline in another method call that constrains the closure).

Can we generally improve the situation for closures somehow? Delay the entire closure's body typeck until after its parent is typecked. So less fixed-point and more "tree of child bodies to typeck". Or do we need the closure body typeck in order for other code in the parent to behave?

@chenyukang

chenyukang commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

Hmm... there are various similar issues with type inference within closures when the closure doesn't have an expectation (e.g. by being inline in another method call that constrains the closure).

Can we generally improve the situation for closures somehow? Delay the entire closure's body typeck until after its parent is typecked. So less fixed-point and more "tree of child bodies to typeck". Or do we need the closure body typeck in order for other code in the parent to behave?

yes, follow your idea we can resolve other issues, such as #63702 and #106138 (both tests are added into this PR).

the current approach creates the closure signature as usual, but delays checking the body when:

  • it is a regular closure, not a coroutine or coroutine closure;
  • at least one input type still contains inference variable

I need more tweak on the code, but generally seems the direction works out

@rust-log-analyzer

This comment has been minimized.

@chenyukang

chenyukang commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

the latest commit

remove the is_let_initializer check to extend the delay of checking to more scenarios, we can handle more cases such as #26735 and #70357, but we get a lot more diagnostics change at the same time, some of them seems better, but some of them seems regression.

@rust-log-analyzer

This comment has been minimized.

@chenyukang
chenyukang force-pushed the yukang-fix-76112-closure-index-inference branch from b7f4faa to 91b7704 Compare August 1, 2026 13:23
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@oli-obk oli-obk added S-waiting-on-t-types Status: Awaiting decision from T-types and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 5, 2026
@oli-obk

oli-obk commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

I'm gonna bring this up as a topic in next week's types team meeting. You're very welcome to join if you want. Gonna mainly talk about the high-level change and how it affects inference and what the issues with it are.

@chenyukang

Copy link
Copy Markdown
Member Author

I'm gonna bring this up as a topic in next week's types team meeting. You're very welcome to join if you want. Gonna mainly talk about the high-level change and how it affects inference and what the issues with it are.

emm, there is a fail in #159829 (comment)

the code can be minized into:

struct Pattern;

fn visit_pattern(_: &Pattern) {}

fn main() {
    let visit_subpattern = |pattern| visit_pattern(pattern);
    let boxed_pattern = Box::new(Pattern);

    // The body constrains `pattern` to `&Pattern`, so this applies a deref coercion.
    visit_subpattern(&boxed_pattern);
    visit_subpattern(&Pattern);
}

Before this change, the closure body is checked first. The call to visit_pattern(pattern) constrains pattern to &Pattern, so &Box<Pattern> is deref-coerced to &Pattern at the first invocation.

With deferred body checking, the first invocation is checked first and fixes the unresolved closure parameter to &Box<Pattern>. The body still type-checks through a deref coercion, but the second invocation fails because &Pattern cannot be coerced back to &Box<Pattern>.

I haven't figured out a solution for this case.

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job aarch64-gnu-llvm-21-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
   Compiling rustc_mir_build v0.0.0 (/checkout/compiler/rustc_mir_build)
error[E0308]: mismatched types
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:894:40
    |
894 |                     visit_subpat(self, subpattern, &user_tys.index(), f);
    |                     ------------       ^^^^^^^^^^ expected `&Box<Pat<'_>>`, found `&Pat<'_>`
    |                     |
    |                     arguments to this function are incorrect
    |
    = note: expected reference `&std::boxed::Box<Pat<'_>>`
               found reference `&Pat<'_>`
note: expected because the closure was earlier called with an argument of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:885:40
    |
885 |                     visit_subpat(self, subpattern, user_tys, f);
    |                     ------------       ^^^^^^^^^^ expected because this argument is of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
    |                     |
    |                     in this closure call
note: closure parameter defined here
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:875:46
    |
875 |         let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
    |                                              ^^^^^^

error[E0308]: mismatched types
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:900:40
    |
900 |                     visit_subpat(self, subpattern, &user_tys.index(), f);
    |                     ------------       ^^^^^^^^^^ expected `&Box<Pat<'_>>`, found `&Pat<'_>`
    |                     |
    |                     arguments to this function are incorrect
    |
    = note: expected reference `&std::boxed::Box<Pat<'_>>`
               found reference `&Pat<'_>`
note: expected because the closure was earlier called with an argument of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:885:40
    |
885 |                     visit_subpat(self, subpattern, user_tys, f);
    |                     ------------       ^^^^^^^^^^ expected because this argument is of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
    |                     |
    |                     in this closure call
note: closure parameter defined here
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:875:46
    |
875 |         let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
    |                                              ^^^^^^

error[E0308]: mismatched types
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:927:40
    |
927 |                     visit_subpat(self, &subpattern.pattern, &subpattern_user_tys, f);
    |                     ------------       ^^^^^^^^^^^^^^^^^^^ expected `&Box<Pat<'_>>`, found `&Pat<'_>`
    |                     |
    |                     arguments to this function are incorrect
    |
    = note: expected reference `&std::boxed::Box<Pat<'_>>`
               found reference `&Pat<'_>`
note: expected because the closure was earlier called with an argument of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:885:40
    |
885 |                     visit_subpat(self, subpattern, user_tys, f);
    |                     ------------       ^^^^^^^^^^ expected because this argument is of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
    |                     |
    |                     in this closure call
note: closure parameter defined here
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:875:46
    |
875 |         let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
    |                                              ^^^^^^

error[E0308]: mismatched types
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:935:40
    |
935 |                     visit_subpat(self, &subpattern.pattern, &subpattern_user_tys, f);
    |                     ------------       ^^^^^^^^^^^^^^^^^^^ expected `&Box<Pat<'_>>`, found `&Pat<'_>`
    |                     |
    |                     arguments to this function are incorrect
    |
    = note: expected reference `&std::boxed::Box<Pat<'_>>`
               found reference `&Pat<'_>`
note: expected because the closure was earlier called with an argument of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:885:40
    |
885 |                     visit_subpat(self, subpattern, user_tys, f);
    |                     ------------       ^^^^^^^^^^ expected because this argument is of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
    |                     |
    |                     in this closure call
note: closure parameter defined here
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:875:46
    |
875 |         let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
    |                                              ^^^^^^

error[E0308]: mismatched types
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:944:40
    |
944 |                     visit_subpat(self, subpattern, user_tys, f);
    |                     ------------       ^^^^^^^^^^ expected `&Box<Pat<'_>>`, found `&Pat<'_>`
    |                     |
    |                     arguments to this function are incorrect
    |
    = note: expected reference `&std::boxed::Box<Pat<'_>>`
               found reference `&Pat<'_>`
note: expected because the closure was earlier called with an argument of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:885:40
    |
885 |                     visit_subpat(self, subpattern, user_tys, f);
    |                     ------------       ^^^^^^^^^^ expected because this argument is of type `&std::boxed::Box<rustc_middle::thir::Pat<'_>>`
    |                     |
    |                     in this closure call
note: closure parameter defined here
   --> compiler/rustc_mir_build/src/builder/matches/mod.rs:875:46
    |
875 |         let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
    |                                              ^^^^^^

For more information about this error, try `rustc --explain E0308`.
[RUSTC-TIMING] rustc_mir_build test:false 5.363
error: could not compile `rustc_mir_build` (lib) due to 5 previous errors

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

Labels

needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-t-types Status: Awaiting decision from T-types T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot infer type when indexing an array inside closure with an argument

5 participants