Skip to content

fix: flatten nested HRTB predicate binders - #23139

Open
YUZHEthefool wants to merge 1 commit into
rust-lang:masterfrom
YUZHEthefool:fix/nested-hrtb-predicate-binders
Open

fix: flatten nested HRTB predicate binders#23139
YUZHEthefool wants to merge 1 commit into
rust-lang:masterfrom
YUZHEthefool:fix/nested-hrtb-predicate-binders

Conversation

@YUZHEthefool

@YUZHEthefool YUZHEthefool commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

两个issue的问题都在于关联类型 bound 递归 lowering 时,clause binder 只记录最内层 lifetime,引用的外层 lifetime 以 ^1/^2 形式逃逸。因此我修复了lower.rs中将 overarching 和内层 HRTB 变量合并到同一个 predicate binder,同时保留dyn Trait了作为真正独立的existential binder。

<The problem is that when the associated type bound is recursively lowering, the clause binder only records the innermost lifetime, and the outer lifetime of the reference escapes in the form of ^1/^2.
So I fix merging overarching and inner HRTB variables into the same predicate binder in lower.rs while retaining the dyn Trait's truly independent existential binder.>

In simpler terms: Outer lifetimes in nested binders are not flattened/renumbered correctly

Fix: #23063 and Fix #23064

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 14, 2026

@dfireBird dfireBird left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for fixing this. I have verified this with rustc and it seems rustc indeed flattens the trait predicate binders into one.

I'll let the implementation details for review with other team members.

View changes since this review

Comment thread crates/hir-ty/src/lower.rs Outdated
@YUZHEthefool
YUZHEthefool force-pushed the fix/nested-hrtb-predicate-binders branch from 8796ae2 to d0a84a4 Compare August 16, 2026 06:23
@YUZHEthefool

Copy link
Copy Markdown
Contributor Author

Now, lifetime search is changed to preferentially match the last name added in the same binder, correctly handle nested lifetime shadowing, and remove the previously incorrect index offset.
And I Improved testing

);
}

#[test]

@ChayimFriedman2 ChayimFriedman2 Aug 27, 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.

The tests shouldn't be in this file, rather in regression.rs.

View changes since the review

}

#[test]
fn nested_hrtb_starts_own_predicate_binder() {

@ChayimFriedman2 ChayimFriedman2 Aug 27, 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 test crosses the border into testing implementation details, I don't think we should have it, though not so sure.

View changes since the review

if let LifetimeRef::Named(lt_name) = &self.store[lifetime] {
self.bound_vars.iter().rev().enumerate().find_map(|(debruijn, (binder, _))| {
binder.iter().enumerate().find_map(|(index, l)| {
binder.iter().enumerate().rev().find_map(|(index, l)| {

@ChayimFriedman2 ChayimFriedman2 Aug 27, 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.

Comment on lines +338 to +339
names.truncate(old_names_len);
*bound_vars = old_bound_vars;

@ChayimFriedman2 ChayimFriedman2 Aug 27, 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.

Pretty sure this is wrong and maybe can cause problems. Why do you truncate here?

View changes since the review

@YUZHEthefool YUZHEthefool Aug 28, 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.

hum...The original plan was:truncate was originally used to restore the parent binder after a generate clause, preventing inner HRTB variables from leaking into sibling bounds.

It feels better to construct a complete flattened binder, temporarily replacing the current binder entry now...
I will fix the above problem together

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.

What's "a sibling bound"? At this level of lowering there is no such thing.

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.

It should said: lower_type_bound should remain state-neutral when returning.🤔

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?

@YUZHEthefool YUZHEthefool Aug 28, 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.

Because the concrete caller is PathLoweringContext::assoc_type_bindings_from_type_bound, the iterates over binding.bounds and invokes lower_type_bound multiple times using the same TyLoweringContext.like:

T: Outer<
    Assoc: for<'a> A<'a> + for<'b> B<'b>
>

Each for<...> only scopes its own TypeBound.
If the binder entry is not restored after lowering A, the context still contains 'a when lowering B.
And, B is lowered with ['a, 'b], and 'b receives index 1, while the correct binder is only ['b], with 'b at index 0.
A local trace with Assoc: for<'a> A<'a> + B confirmed the contamination like:

with restoration:    A = ['a], B = []
without restoration: A = ['a], B = ['a]

So I think restoring the previous entry is required to preserve the lexical scope of each TypeBound🤔
But there seems also have a problem now, because currently I only create an empty predicate frame when the top-level bound itself is ForLifetime.

I used DeepL to translate some of the content, but after reading it over, I think it should match what I intended to say :)

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.

It's still not correct in this case because the latter bound will override the BoundVarKinds for the earlier bound.

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.

Projects

None yet

4 participants