Skip to content

diagnostics: Suggest fn binding type for unstable closure for<> binders - #160478

Merged
rust-bors[bot] merged 3 commits into
rust-lang:mainfrom
Dnreikronos:diagnostics/closure_lifetime_binder_sugg
Aug 17, 2026
Merged

diagnostics: Suggest fn binding type for unstable closure for<> binders#160478
rust-bors[bot] merged 3 commits into
rust-lang:mainfrom
Dnreikronos:diagnostics/closure_lifetime_binder_sugg

Conversation

@Dnreikronos

@Dnreikronos Dnreikronos commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

View all comments

Fixes #160431

for<'a> |x: &'a T| -> U { ... } currently just says "consider removing for<...>", which idk is kinda useless when someone landed there while fighting HRTB errors. This rewrites the obvious case to let cl: for<'a> fn(...) = |x| { ... } instead.

Visitor emits the gate so we have AST context for the rewrite, and we still keep a pre-expansion fallback for #[cfg(false)] etc. MachineApplicable only when we're pretty sure: by-value bindings, lifetime-only unbounded binders, no _ in the sig, no macro expansion. Capture checking is a conservative single-segment free-path heuristic since this runs pre-resolution/pre-typeck; when that looks uncertain we fall back to MaybeIncorrect (or just "consider removing"). imo that's the right tradeoff here: better a maybe-wrong help than rustfix auto-applying into E0308. fyi free-fn / None / Some cases also hit MaybeIncorrect for the same reason.

ltm if the heuristic feels too conservative and y'all want it tightened later.

@rustbot

rustbot commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

The parser was modified, potentially altering the grammar of (stable) Rust
which would be a breaking change.

cc @fmease

@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 Aug 4, 2026
@rustbot

rustbot commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

r? @JonathanBrouwer

rustbot has assigned @JonathanBrouwer.
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 19 candidates

@rustbot

This comment has been minimized.

@Dnreikronos
Dnreikronos force-pushed the diagnostics/closure_lifetime_binder_sugg branch from b3e5c81 to 8a03a04 Compare August 4, 2026 01:38
@Dnreikronos
Dnreikronos marked this pull request as draft August 4, 2026 01:42
@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 Aug 4, 2026
@rust-bors

This comment has been minimized.

@Dnreikronos
Dnreikronos force-pushed the diagnostics/closure_lifetime_binder_sugg branch from 8a03a04 to 1fccd4c Compare August 4, 2026 13:02
@Dnreikronos
Dnreikronos marked this pull request as ready for review August 4, 2026 13:08
@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 4, 2026
@rust-bors

This comment has been minimized.

When `for<'a>` appears on a closure without `closure_lifetime_binder`,
suggest rewriting a simple let-bound closure to a stable
`for<'a> fn(...)` binding type annotation when that rewrite is safe.
Cover MachineApplicable rewrites, MaybeIncorrect capture cases, and
macro-expanded binders where structured suggestions must be suppressed.
@Dnreikronos
Dnreikronos force-pushed the diagnostics/closure_lifetime_binder_sugg branch from 1fccd4c to 7b0d9e4 Compare August 10, 2026 17:13
@rustbot

rustbot commented Aug 10, 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.

@JonathanBrouwer

JonathanBrouwer commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

This looks cool but I might not have time to review this in the next 2 weeks as it would require me to do some reading about how for<> binders are implemented
@rustbot reroll

@rustbot rustbot assigned mejrs and unassigned JonathanBrouwer Aug 10, 2026
@JonathanBrouwer

JonathanBrouwer commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

I will do a perf run, just in case, since this does some extra work
@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 10, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 10, 2026
…er_sugg, r=<try>

diagnostics: Suggest fn binding type for unstable closure for<> binders
@rust-bors

rust-bors Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 44fa895 (44fa895cdd61615981ffd0c4e535670c534b839a)
Base parent: ef20314 (ef20314466010b8b9259ec5f86230c530ca08661)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (44fa895): 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.6%)

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)
1.0% [0.4%, 2.7%] 5
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.4% [-5.7%, -0.4%] 9
All ❌✅ (primary) - - 0

Cycles

Results (primary 2.6%, secondary -0.2%)

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

mean range count
Regressions ❌
(primary)
2.6% [2.6%, 2.6%] 1
Regressions ❌
(secondary)
0.8% [0.4%, 2.3%] 10
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.8% [-3.0%, -0.4%] 17
All ❌✅ (primary) 2.6% [2.6%, 2.6%] 1

Binary size

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

Bootstrap: 455.478s -> 454.351s (-0.25%)
Artifact size: 398.50 MiB -> 398.45 MiB (-0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 10, 2026
@oli-obk

This comment was marked as resolved.

@oli-obk oli-obk closed this Aug 13, 2026
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 13, 2026
@oli-obk oli-obk reopened this Aug 13, 2026
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 13, 2026
@rust-bors

This comment has been minimized.

@mejrs mejrs 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.

This PR looks highly overengineered to me. I'd suggest to undo the changes and just change the error message, like

    gate_all!(
        closure_lifetime_binder,
        "`for<...>` binders for closures are experimental",
-        "consider removing `for<...>`"
+        "consider using a type annotation instead : `let closure: for<...> fn(...) -> ... = /* closure */;`"
    );

It's not as good a suggestion as you've written here, but the implementation is trivial.

View changes since this review

@@ -0,0 +1,34 @@
//@ edition: 2024
//@ compile-flags: --error-format=json

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.

Why are you using error-format=json here? Same for the other tests.

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.

yeah that was just so i could check applicability (MaybeIncorrect vs MachineApplicable). normal stderr doesn't print that, so i dumped the raw json and grepped the field. I agree that is kinda ugly, and those tests only existed for the structured suggestion anyway.
I will dropping them!

Comment thread compiler/rustc_parse/src/parser/expr.rs Outdated
Comment on lines +2466 to +2467
// Pre-expansion gate so `#[cfg(false)]` code is still rejected. The post-expansion
// visitor may replace this with a richer diagnostic when the AST is available.

@mejrs mejrs Aug 16, 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.

This comment is redundant, that's the point of using GatedSpans.

@Dnreikronos Dnreikronos Aug 16, 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.

yeah fair point. I was just reminding myself why the parser still records the span and that's already what GatedSpans is for, so I agree that the comment is noise. I'm removing it rn.

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.

This sort of complexity should not be added in a place like this - I will not accept any changes to the feature gating logic that are not either ~obviously correct or absolutely necessary.

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.

i know, this is too much for this file.

gate_all! only has the span, so i can't tell if it's a let cl = for<'a> |...| we could rewrite. that's why i gated in the visitor (where i have the AST) and then still had to emit something for spans that never show up after expansion, like #[cfg(false)]. that's how the extra bookkeeping showed up.

imo the rewrite is nicer, but I agree that it's not worth bending the gate logic for a help message.

@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 Aug 16, 2026
@rustbot

rustbot commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

The visitor-based rewrite needed extra gate bookkeeping for
cfg'd-out spans and was too much complexity for feature_gate.
Keep gate_all! and point people at a for<> fn type annotation
instead.
@Dnreikronos
Dnreikronos force-pushed the diagnostics/closure_lifetime_binder_sugg branch from 7b0d9e4 to 916b10c Compare August 16, 2026 16:02
@Dnreikronos

Copy link
Copy Markdown
Contributor Author

@mejrs, please let me know if you still think that something isn't good!

@Dnreikronos

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@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 16, 2026

@mejrs mejrs 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.

@rust-bors

rust-bors Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 916b10c has been approved by mejrs

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 16, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Aug 16, 2026
…fetime_binder_sugg, r=mejrs

diagnostics: Suggest fn binding type for unstable closure for<> binders

Fixes rust-lang#160431

`for<'a> |x: &'a T| -> U { ... }` currently just says "consider removing for<...>", which idk is kinda useless when someone landed there while fighting HRTB errors. This rewrites the obvious case to `let cl: for<'a> fn(...) = |x| { ... }` instead.

Visitor emits the gate so we have AST context for the rewrite, and we still keep a pre-expansion fallback for `#[cfg(false)]` etc. MachineApplicable only when we're pretty sure: by-value bindings, lifetime-only unbounded binders, no `_` in the sig, no macro expansion. Capture checking is a conservative single-segment free-path heuristic since this runs pre-resolution/pre-typeck; when that looks uncertain we fall back to MaybeIncorrect (or just "consider removing"). imo that's the right tradeoff here: better a maybe-wrong help than rustfix auto-applying into E0308. fyi free-fn / `None` / `Some` cases also hit MaybeIncorrect for the same reason.

ltm if the heuristic feels too conservative and y'all want it tightened later.
rust-bors Bot pushed a commit that referenced this pull request Aug 16, 2026
…uwer

Rollup of 17 pull requests

Successful merges:

 - #161017 (Library: enforce clippy deref lints in CI)
 - #160416 (std: fix unix socket address truncation without a trailing NUL)
 - #161006 ([CI] Build newer `binutils` before building `gcc`)
 - #161141 (Add documentation for BPF targets)
 - #161157 (bootstrap: Move several items out of the crate root)
 - #161185 (std: guard against unwinds in queue-based `Once`)
 - #161186 (miri subtree update)
 - #159855 (std: retry waitid on EINTR in the pidfd wait path)
 - #160478 (diagnostics: Suggest fn binding type for unstable closure for<> binders)
 - #161053 (Add regression test for borrow of array drop type in const)
 - #161073 (Add regression test for path printing with infinitely many visible names)
 - #161099 (Add regression test for unstable def_ident_span fingerprint with incremental recompilation)
 - #161103 (cleanup: rip out unnecessary `iter().last()` and `iter().next()`)
 - #161136 (Add BPF test for Rust ABI stack arguments)
 - #161146 (Switch to c8a EC2 runner for auto merges)
 - #161148 ([rustdoc] Put back one removed flaky GUI test (which hopefully isn't flaky anymore))
 - #161181 (Add back flaky gui rustdoc test `tests/rustdoc-gui/headers-color.goml`)
@rust-bors
rust-bors Bot merged commit 75b2534 into rust-lang:main Aug 17, 2026
13 checks passed
@rustbot rustbot added this to the 1.100.0 milestone Aug 17, 2026
rust-bors Bot pushed a commit that referenced this pull request Aug 17, 2026
Rollup merge of #160478 - Dnreikronos:diagnostics/closure_lifetime_binder_sugg, r=mejrs

diagnostics: Suggest fn binding type for unstable closure for<> binders

Fixes #160431

`for<'a> |x: &'a T| -> U { ... }` currently just says "consider removing for<...>", which idk is kinda useless when someone landed there while fighting HRTB errors. This rewrites the obvious case to `let cl: for<'a> fn(...) = |x| { ... }` instead.

Visitor emits the gate so we have AST context for the rewrite, and we still keep a pre-expansion fallback for `#[cfg(false)]` etc. MachineApplicable only when we're pretty sure: by-value bindings, lifetime-only unbounded binders, no `_` in the sig, no macro expansion. Capture checking is a conservative single-segment free-path heuristic since this runs pre-resolution/pre-typeck; when that looks uncertain we fall back to MaybeIncorrect (or just "consider removing"). imo that's the right tradeoff here: better a maybe-wrong help than rustfix auto-applying into E0308. fyi free-fn / `None` / `Some` cases also hit MaybeIncorrect for the same reason.

ltm if the heuristic feels too conservative and y'all want it tightened later.
RalfJung pushed a commit to RalfJung/miri that referenced this pull request Aug 17, 2026
…uwer

Rollup of 17 pull requests

Successful merges:

 - rust-lang/rust#161017 (Library: enforce clippy deref lints in CI)
 - rust-lang/rust#160416 (std: fix unix socket address truncation without a trailing NUL)
 - rust-lang/rust#161006 ([CI] Build newer `binutils` before building `gcc`)
 - rust-lang/rust#161141 (Add documentation for BPF targets)
 - rust-lang/rust#161157 (bootstrap: Move several items out of the crate root)
 - rust-lang/rust#161185 (std: guard against unwinds in queue-based `Once`)
 - rust-lang/rust#161186 (miri subtree update)
 - rust-lang/rust#159855 (std: retry waitid on EINTR in the pidfd wait path)
 - rust-lang/rust#160478 (diagnostics: Suggest fn binding type for unstable closure for<> binders)
 - rust-lang/rust#161053 (Add regression test for borrow of array drop type in const)
 - rust-lang/rust#161073 (Add regression test for path printing with infinitely many visible names)
 - rust-lang/rust#161099 (Add regression test for unstable def_ident_span fingerprint with incremental recompilation)
 - rust-lang/rust#161103 (cleanup: rip out unnecessary `iter().last()` and `iter().next()`)
 - rust-lang/rust#161136 (Add BPF test for Rust ABI stack arguments)
 - rust-lang/rust#161146 (Switch to c8a EC2 runner for auto merges)
 - rust-lang/rust#161148 ([rustdoc] Put back one removed flaky GUI test (which hopefully isn't flaky anymore))
 - rust-lang/rust#161181 (Add back flaky gui rustdoc test `tests/rustdoc-gui/headers-color.goml`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. 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.

When encountering for<'a> in closure declaration, try to provide structured suggestion to use a binding with a type

6 participants