Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exponential compile times for nested struct with associated type and generics #105900

Open
alansartorio opened this issue Dec 19, 2022 · 2 comments
Labels
C-bug Category: This is a bug. I-compiletime Issue: Problems and improvements with respect to compile times.

Comments

@alansartorio
Copy link

I tried this code:

use std::marker::PhantomData;

pub trait Wrappable<I>: Sized {
    type Type;
    fn wrap(self) -> Wrapper<Self, Self::Type> {
        Wrapper(self, PhantomData)
    }
}

pub struct Wrapper<A, E>(A, PhantomData<E>);
impl<I, E, A: Wrappable<I, Type = E>> Wrappable<I> for Wrapper<A, E> {
    type Type = E;
}

impl Wrappable<u8> for u8 {
    type Type = u8;
}

pub fn function() {
    0u8
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap()
        .wrap();
}

And for each wrap I add, compilation times and memory usage double unexpectedly.

When I run RUSTFLAGS="-Z time-passes" cargo check I get this:

    Blocking waiting for file lock on build directory
    Checking slow_compile v0.1.0 (/####/slow_compile)
time:   0.000; rss:   36MB ->   39MB (   +3MB)        parse_crate
time:   0.001; rss:   41MB ->   58MB (  +16MB)        expand_crate
time:   0.001; rss:   41MB ->   58MB (  +16MB)        macro_expand_crate
time:   0.000; rss:   58MB ->   61MB (   +3MB)        late_resolve_crate
time:   0.000; rss:   58MB ->   61MB (   +3MB)        resolve_crate
time:   0.002; rss:   41MB ->   61MB (  +19MB)        configure_and_expand
time:   0.000; rss:   62MB ->   65MB (   +3MB)        misc_checking_1
time:   0.000; rss:   65MB ->   69MB (   +4MB)        type_collecting
time:  13.023; rss:   69MB -> 2788MB (+2719MB)        item_bodies_checking
time:  13.023; rss:   65MB -> 2788MB (+2722MB)        type_check_crate
time:   0.002; rss: 2788MB -> 2790MB (   +3MB)        MIR_borrow_checking
time:   0.004; rss: 2790MB -> 2811MB (  +21MB)        crate_lints
time:   0.004; rss: 2790MB -> 2811MB (  +21MB)        lint_checking
time:   0.004; rss: 2790MB -> 2811MB (  +21MB)        misc_checking_3
time:   0.046; rss: 2811MB -> 2811MB (   +0MB)        codegen_crate
time:   0.001; rss: 2811MB -> 2815MB (   +4MB)        incr_comp_persist_result_cache
time:   0.001; rss: 2811MB -> 2815MB (   +4MB)        serialize_dep_graph
time:   0.042; rss: 2815MB -> 1809MB (-1006MB)        free_global_ctxt
time:  13.200; rss:   28MB ->   95MB (  +67MB)        total
    Finished dev [unoptimized + debuginfo] target(s) in 13.86s

Here's a playground where you can reproduce it:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=db81287cfac9166b22476eb739703812

I got this code by reducing a parser I was writing using the chumsky crate:

use chumsky::prelude::*;

pub fn parser() -> impl Parser<char, (), Error = Simple<char>> {
    // Time to compile gets quicker when I replace line 7 with:
    //just::<char, _, _>("a")

    just("a")
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        .then(just("a"))
        // Each call I add increases cargo check time by a factor of x10
        //  and memory usage by x2
        //.then(just("a"))
        .ignored()
}

I checked some similar issues but I couldn't find the same problem I have:

Meta

rustc --version --verbose:

rustc 1.68.0-nightly (d0dc9efff 2022-12-18)
binary: rustc
commit-hash: d0dc9efff14ac0a1eeceffd1e605e37eeb8362a0
commit-date: 2022-12-18
host: x86_64-unknown-linux-gnu
release: 1.68.0-nightly
LLVM version: 15.0.6

@alansartorio alansartorio added the C-bug Category: This is a bug. label Dec 19, 2022
@jruderman
Copy link
Contributor

@rustbot label +I-compiletime

@rustbot rustbot added the I-compiletime Issue: Problems and improvements with respect to compile times. label Dec 19, 2022
@Shfty
Copy link

Shfty commented Apr 20, 2023

I'm seeing similar when using a lazy state monad implemented at the type level, where each step in the binding chain introduces multiple layers of nesting to the final thunk struct.

This quickly adds up: The pure functional equivalent of binding 3 ZST-tagged variables, feeding them to a function, and storing back its output compiles in ~0.5sec, but balloons up to ~150sec with a chain of twice that length.

github-merge-queue bot pushed a commit to linebender/xilem that referenced this issue Jun 28, 2024
This ports xilem_web to the new xilem_core.

There's also a lot of cleanup internally:
* Get rid of all of the complex macros to support DOM interfaces, and
instead use associated type bounds on the `View::Element`.
* Introduce an extendable modifier based system, which should also work
on top of memoization (`Arc`, `Memoize`) and `OneOf` views with an
intersection of the modifiable properties.
* This modifier based system gets rid of the hacky way to propagate
attributes to elements, and takes inspiration by masonrys `WidgetMut`
type to apply changes.
* Currently that means `Attributes`, `Classes` and `Styles` to reflect
what xilem_web previously offered.

Downsides (currently, needs some investigation):

~~Due to more internal type complexity via associated types this suffers
from rust-lang/rust#105900. The new trait
solver should hopefully mitigate some of that, but it seems currently it
completely stalls in the todomvc example (not in other examples).~~
~~The deep, possibly completely static composition via associated
type-bounds of the view and element tree unfortunately can take some
time to compile, this gets (already) obvious in the todomvc example. The
other examples don't seem to suffer that bad yet from that issue,
probably because they're quite simple.~~

~~I really hope we can mitigate this mostly, because I think this is the
idiomatic (and more correct) way to implement what the previous API has
offered.~~

One idea is to add a `Box<dyn AnyViewSequence>`, as every element takes
a "type-erased" `ViewSequence` as parameter, so this may solve most of
the issues (at the slight cost of dynamic dispatch/allocations).

Edit: idea was mostly successful, see comment right below.

I think it also closes #274

It's a draft, as there's a lot of changes in xilem_core that should be
upstreamed (and cleaned up) via separate PRs and I would like to
(mostly) fix the slow-compile time issue.

---------

Co-authored-by: Daniel McNab <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. I-compiletime Issue: Problems and improvements with respect to compile times.
Projects
None yet
Development

No branches or pull requests

4 participants