Skip to content

Don't list escaping bound regions in nested for<...> binders of E0308 notes - #159232

Open
Rani367 wants to merge 1 commit into
rust-lang:mainfrom
Rani367:fix-nested-hrtb-binder-printing
Open

Don't list escaping bound regions in nested for<...> binders of E0308 notes#159232
Rani367 wants to merge 1 commit into
rust-lang:mainfrom
Rani367:fix-nested-hrtb-binder-printing

Conversation

@Rani367

@Rani367 Rani367 commented Jul 13, 2026

Copy link
Copy Markdown

The expected/found notes of "one type is more general than the other" errors could print types that are not even valid syntax, repeating lifetimes from an outer binder inside nested for<...> lists:

= note: expected mutable reference `&mut for<'a> fn(for<'a> fn(&'a ()))`
           found mutable reference `&mut fn(fn(&()))`

The expected type here is actually &mut for<'a> fn(fn(&'a ())), the inner fn pointer binds nothing. In the example from #111365 the note printed for<'o> fn(for<'a, 'o> fn(&'a (), &'o ())) even though the inner binder only binds 'a.

The cause is in how cmp_fn_sig builds its for<...> prefixes. It calls name_all_regions and joins every region of the returned map into the list. But RegionFolder records every region at or above the depth of the binder being printed, so regions that are bound by an enclosing binder and merely escape through the current one also end up in the map. The pretty printer's own text output already skips those regions (which is why the labels of these diagnostics were correct, see #102392), but the returned map kept them, and cmp_fn_sig recurses through nested fn pointers one binder at a time, so every nested for<...> list picked up all the outer lifetimes.

This PR keys the folder's map by the region's binder offset and filters the map that name_all_regions returns down to the regions actually bound by the binder being named (offset 0). Escaping regions stay in the folder's internal map so that repeated occurrences keep getting the same name, and the emitted text is untouched. Keying by offset also fixes a latent collision where a bound region and an escaping region with the same bound variable index would share a single map entry.

With this change, the two examples above print:

= note: expected mutable reference `&mut for<'a> fn(fn(&'a ()))`
           found mutable reference `&mut fn(fn(&()))`
= note: expected fn pointer `for<'o> fn(for<'a> fn(&'a (), &'o ()))`
           found fn pointer `fn(for<'a> fn(&'a (), &'a ()))`

The found line of tests/ui/nll/relate_tys/placeholder-outlives-existential.rs also loses its leaked binders and now matches the type written in the test's source.

Fixes #134410

This also fixes the leaked outer lifetimes reported in #111365, but I left that issue open since it additionally asks for renaming of shadowed lifetimes (for<'a> fn(for<'a> ...) for two distinct lifetimes both named 'a in the source), which is a separate problem.

…08 notes

The `for<...>` prefixes in `cmp_fn_sig` are built from the region map
returned by `name_all_regions`. That map also contained regions that are
bound by an enclosing binder and merely escape through the binder being
named, so nested binders in expected/found notes listed lifetimes they
don't bind, printing invalid types such as
`&mut for<'a> fn(for<'a> fn(&'a ()))` for `&mut for<'a> fn(fn(&'a ()))`.

Key the folder's map by the region's binder offset and only return the
regions actually bound by the binder being named. The offset in the key
also fixes a latent collision between a bound and an escaping region
sharing the same bound variable index. The printed text is unaffected:
the `name` closure already skips escaping regions when writing to the
printer, which is why diagnostic labels were already correct.
@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 Jul 13, 2026
@rustbot

rustbot commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the pull request, and welcome! The Rust Project is excited to review your changes, and you should hear from @fee1-dead (or someone else) some time within the next two weeks.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue
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 17 candidates

@fee1-dead

Copy link
Copy Markdown
Member

Binders make me a bit dizzy, so...

@rustbot reroll

@rustbot rustbot assigned petrochenkov and unassigned fee1-dead Jul 22, 2026
@petrochenkov

Copy link
Copy Markdown
Contributor

r? types

@rustbot rustbot added the T-types Relevant to the types team, which will review and decide on the PR/issue. label Jul 22, 2026
@rustbot rustbot assigned jackh726 and unassigned petrochenkov Jul 22, 2026
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-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.

Error mentions invalid nested HRTB: &mut for<'a> fn(for<'a> fn(&'a ()))

5 participants