charon icon indicating copy to clipboard operation
charon copied to clipboard

Bug: quadratic time on deeply nested types

Open Nadrieril opened this issue 10 months ago • 3 comments

#![allow(unused)]
fn main() {}

trait Iterator {
    fn chain<P>(self, p: P) -> Chain<Self, P>
    where
        Self: Sized,
    {
        Chain(self, p)
    }
}

struct Chain<L, R>(L, R);

impl<L, R> Iterator for Chain<L, R> {}

fn test() {
    Chain(0, 0)
        //
        .chain(0)
        .chain(0)
        .chain(0)
        .chain(0)
        .chain(0)
        .chain(0);
}

This takes time worse than quadratic in the number of calls to chain, even with --no-serialize --hide-marker-traits --ullbc. This goes away if we do struct Chain<L: ?Sized, R: ?Sized>(PhantomData<L>, PhantomData<R>);.

This is therefore caused by the quadratically-sized annoyance of Sized trait bounds:

let x = Chain(0, 0).chain(0).chain(0);

we get the following type for x:

Chain<
    Chain<
        Chain<i32, i32>[core::marker::Sized<i32>, core::marker::Sized<i32>],
        i32
    >[
        core::marker::Sized<Chain<i32, i32>[core::marker::Sized<i32>, core::marker::Sized<i32>]>,
        core::marker::Sized<i32>
    ],
    i32
>[
    core::marker::Sized<
        Chain<
            Chain<i32, i32>[core::marker::Sized<i32>, core::marker::Sized<i32>],
            i32
        >[
            core::marker::Sized<Chain<i32, i32>[core::marker::Sized<i32>, core::marker::Sized<i32>]>,
            core::marker::Sized<i32>
        ]
    >,
    core::marker::Sized<i32>
]

Nadrieril avatar Feb 17 '25 13:02 Nadrieril

This is annoying. Do you have an idea about how to solve this?

sonmarcho avatar Feb 17 '25 14:02 sonmarcho

We'll have to do more deduplication, in particular for passes that traverse a type.

Nadrieril avatar Feb 17 '25 14:02 Nadrieril

Actually the first step would be to hash-cons TraitRefs in both hax and charon so we can piggy-back on rustc's interning to only translate each ref once.

Second step would be to deduplicate the serialized output so we only serialize each unique Ty/TraitRef once. Maybe with a flag to recover the plain serialization for easier debugging (that's exactly what hax does).

Third step would be to somehow avoid applying the same modification to a type multiple times. That sounds very tricky; possibly I might need to unify the various type-modifying passes first so that common logic can be reused. Expect binder shenanigans.

Nadrieril avatar Oct 22 '25 18:10 Nadrieril