Skip to content

Anonymize inner binders when checking refining - #165222

Open
mu001999 wants to merge 1 commit into
rust-lang:mainfrom
mu001999-contrib:fix-165091
Open

mu001999 wants to merge 1 commit into
rust-lang:mainfrom
mu001999-contrib:fix-165091

Conversation

@mu001999

@mu001999 mu001999 commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Fix #165091

The inner binder is missed.

@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 Oct 31, 2026
@mu001999
mu001999 marked this pull request as ready for review October 31, 2026 13:47
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 31, 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 Oct 31, 2026
@rustbot

This comment was marked as outdated.

@fmease fmease assigned fmease and unassigned beetrees Nov 3, 2026

@fmease fmease 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 working on this! I got a smallish request :)

View changes since this review

T: TypeFoldable<TyCtxt<'tcx>>,
{
self.tcx.anonymize_bound_vars(t)
self.tcx.anonymize_bound_vars(t.super_fold_with(self))

@fmease fmease Nov 4, 2026 •

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.

This implementation is obviously correct but it's doing a lot unnecessary walking & work. Of course, this part of the code isn't performance critical & I don't think we have any benchmarks containing RPITITs. Still, IMO we should simplify this, it won't take that much more code.


As you know, anonymize_bound_vars only anonymizes bound vars bound by the current binder & under the hood it's also a TypeFolder (with some TypeFlags and other early returns) meaning in total we rewalk the type a lot if there are several binders.

Moreover, the BoundVarReplacer (a folder) inside anonymize_bound_vars performs a lot of checks to ensure it only replaces bound vars that are bound at the correct level & it has to shift the de-Bruijn indices & re-intern the shifted regions etc. produced by anonymize_bound_vars's BoundVarReplacerDelegate.

However, since the idea of our Anonymize here is to fully anonymize the type, most of these things don't need to be done. Fundamentally we simply we want to replace any Bound{Region,Ty,Const}Kind with Anon keeping the de Bruijn index & the BoundVar (index) fully intact.


So this whole operation could just be a TypeFolder that impls fold_{region,type,const}, returns the argument unchanged if it !_.has_bound_vars() otherwise if it's a ty::{,Re,ConstKind::}Bound return a *Bound with *Kind::Anon.

@fmease fmease Nov 4, 2026 •

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.

E.g., for code like

#![deny(refining_impl_trait)]
trait A {
    fn f() -> impl B<for<'a> fn(for<'b> fn(&'a (), &'b ()))>;
}
impl A for () {
    fn f() -> impl B<for<'a> fn(for<'b> fn(&'a (), &'b ()))> {
        0
    }
}
trait B<T: ?Sized> {}
impl<T: ?Sized, U: ?Sized> B<T> for U {}

the execution trace under your PR looks something like

fold_binder()
    # arg: Binder { value: TraitClause(<Opaque(DefId(0:10 ~ b[656a]::{impl#0}::f::{opaque#0}), []) as B<for<Region(BrNamed(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)))> fn(for<Region(BrNamed(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))> fn(&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) ()))>>, polarity:Positive), bound_vars: [] }
    # arg has bound vars; calling super_fold_with()...
    fold_ty()
        # arg: Alias(Yes, Alias { kind: Opaque { def_id: DefId(0:10 ~ b[656a]::{impl#0}::f::{opaque#0}) }, args: [], .. })
        # arg has no bound vars; ret arg as is...
    fold_ty()
        # arg: Binder { value: fn(Binder { value: fn(&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) ()), bound_vars: [Region(BrNamed(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))] }), bound_vars: [Region(BrNamed(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)))] }
        # arg has bound vars; calling super_fold_with()...
        fold_binder()
            # arg: Binder { value: FnSigTys { inputs_and_output: [Binder { value: fn(&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) ()), bound_vars: [Region(BrNamed(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))] }, ()] }, bound_vars: [Region(BrNamed(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)))] }
            # arg has bound vars; calling super_fold_with()...
            fold_ty()
                # arg: Binder { value: fn(&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) ()), bound_vars: [Region(BrNamed(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))] }
                # arg has bound vars; calling super_fold_with()...
                fold_binder()
                    # arg: Binder { value: FnSigTys { inputs_and_output: [&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) (), ()] }, bound_vars: [Region(BrNamed(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))] }
                    # arg has bound vars; calling super_fold_with()...
                    fold_ty()
                        # arg: &'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) ()
                        # arg has bound vars; calling super_fold_with()...
                        fold_ty()
                            # arg: ()
                            # arg has no bound vars; ret arg as is...
                    fold_ty()
                        # arg: &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) ()
                        # arg has bound vars; calling super_fold_with()...
                        fold_ty()
                            # arg: ()
                            # arg has no bound vars; ret arg as is...
                    fold_ty()
                        # arg: ()
                        # arg has no bound vars; ret arg as is...
                    anonymize_bound_vars()
                        # arg: Binder { value: FnSigTys { inputs_and_output: [&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) (), ()] }, bound_vars: [Region(BrNamed(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))] }
                        # has esc bound vars; calling arg.fold_with(BVR)...
                            BVR::fold_ty()
                                # arg: &'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) ()
                                # has vars bound at/above current index
                                # calling super_fold_with()...
                                BVR::fold_region()
                                    # arg: '^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a))
                                    # not ReBound with matching index; returning arg as is...
                                BVR::fold_ty()
                                    # arg: ()
                                    # no vars bound at/above current index; ret arg as is...
                            BVR::fold_ty()
                                # arg: &'^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)) ()
                                # has vars bound at/above current index
                                # calling super_fold_with()...
                                BVR::fold_region()
                                    # arg: '^0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b))
                                    # is ReBound with matching index
                                    # calling delegate.replace_region(0.Named(DefId(0:7 ~ b[656a]::A::f::{opaque#0}::'b)))...
                                    # ...which yielded '^0
                                    # adjusting the de Bruijn index...
                                    # ...giving us '^0
                                BVR::fold_ty()
                                    # arg: ()
                                    # no vars bound at/above current index; ret arg as is...
                            BVR::fold_ty()
                                # arg: ()
                                # no vars bound at/above current index; ret arg as is...
            fold_ty()
                # arg: ()
                # arg has no bound vars; ret arg as is...
            anonymize_bound_vars()
                # arg: Binder { value: FnSigTys { inputs_and_output: [Binder { value: fn(&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0 ()), bound_vars: [Region(BrAnon)] }, ()] }, bound_vars: [Region(BrNamed(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)))] }
                # has esc bound vars; calling arg.fold_with(BVR)...
                    BVR::fold_ty()
                        # arg: Binder { value: fn(&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0 ()), bound_vars: [Region(BrAnon)] }
                        # has vars bound at/above current index
                        # calling super_fold_with()...
                        BVR::fold_binder()
                            # arg: Binder { value: FnSigTys { inputs_and_output: [&'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) (), &'^0 (), ()] }, bound_vars: [Region(BrAnon)] }
                            # calling super_fold_with()...
                            BVR::fold_ty()
                                # arg: &'^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)) ()
                                # has vars bound at/above current index
                                # calling super_fold_with()...
                                BVR::fold_region()
                                    # arg: '^1_0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a))
                                    # is ReBound with matching index
                                    # calling delegate.replace_region(0.Named(DefId(0:6 ~ b[656a]::A::f::{opaque#0}::'a)))...
                                    # ...which yielded '^0
                                    # adjusting the de Bruijn index...
                                    # ...giving us '^1_0
                                BVR::fold_ty()
                                    # arg: ()
                                    # no vars bound at/above current index; ret arg as is...
                            BVR::fold_ty()
                                # arg: &'^0 ()
                                # no vars bound at/above current index; ret arg as is...
                            BVR::fold_ty()
                                # arg: ()
                                # no vars bound at/above current index; ret arg as is...
                    BVR::fold_ty()
                        # arg: ()
                        # no vars bound at/above current index; ret arg as is...
    anonymize_bound_vars()
        # arg: Binder { value: TraitClause(<Opaque(DefId(0:10 ~ b[656a]::{impl#0}::f::{opaque#0}), []) as B<for<Region(BrAnon)> fn(for<Region(BrAnon)> fn(&'^1_0 (), &'^0 ()))>>, polarity:Positive), bound_vars: [] }
        # no esc bound vars; ret arg...

See how it walks into parts of the type just to walk out and reenter parts it's already visited & partially anonymized (but now under different Binder.bound_vars / at a different binder depth)?

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

rustbot commented Nov 5, 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.

This branch has not been deployed

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

Labels

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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refining_impl_trait lint false positive when nesting Fn inside impl Future

4 participants