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

Where clauses increase compilation time and memory use dramatically. #26325

Closed
paholg opened this issue Jun 15, 2015 · 9 comments
Closed

Where clauses increase compilation time and memory use dramatically. #26325

paholg opened this issue Jun 15, 2015 · 9 comments
Labels
A-trait-system Area: Trait system I-slow Issue: Problems and improvements with respect to performance of generated code. P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@paholg
Copy link

paholg commented Jun 15, 2015

When implementing a trait with a member function (vs. one without), the compiler requires more information and takes much longer to compile.

Consider the following two implementations of addition over Peano numbers as types:

First, using std::ops::Add: http://is.gd/7tO1cK

Second, using a custom trait for addition, AddPeano, that has only an associated type and no member functions: http://is.gd/byhqSn

The key difference is here:

Using AddPeano,

impl<Lhs: Peano + AddPeano<Rhs>, Rhs: Peano> AddPeano<Rhs> for Succ<Lhs> {
    type Output = Succ<<Lhs as AddPeano<Rhs>>::Output>;
}

Using std::ops::Add,

impl<Lhs: Peano + Add<Rhs>, Rhs: Peano> Add<Rhs> for Succ<Lhs> where <Lhs as Add<Rhs>>::Output: Peano {
    type Output = Succ<<Lhs as Add<Rhs>>::Output>;
    fn add(self, rhs: Rhs) -> Self::Output { unreachable!() }
}

Without the where clause, the compiler gives the error

error: the trait `Peano` is not implemented for the type `<Lhs as core::ops::Add<Rhs>>::Output`

In main() for both of those examples, the Add traits are used for successive doubling until the number 32 is reached. Using AddPeano, playpen finishes in moments, whereas using Add it triggers playpen's timeout and fails to compile.

I have had similar results locally ... even using up 8 GB of ram in the case of the std::ops version while the version without a function would compile virtually instantly.

@Aatch
Copy link
Contributor

Aatch commented Jun 16, 2015

It actually has nothing to do with the presence/absence of a method, this compiles in a similar amount of time: https://play.rust-lang.org/?gist=d9c18a3c48d6c0584579&version=nightly

It's the where clause, which, yes, is required in this case, but only due to the method returning Self::Output. I've updated the title of the issue to better reflect this.

@Aatch Aatch changed the title The compiler requires more information and can take much longer to compile when implementing a trait with a member function vs. one without. Where clauses increase compilation time and memory use dramatically. Jun 16, 2015
@Aatch Aatch added the I-slow Issue: Problems and improvements with respect to performance of generated code. label Jun 16, 2015
@paholg
Copy link
Author

paholg commented Jun 16, 2015

@Aatch I didn't think to specifically test whether it was the added information in the where clause or the function that made it slower, but that makes sense.

The big question to me is why does the function having output type Self::Output cause the compiler to be confused and require the statement in the where clause?

In the AddPeano example, we still do Succ<<Lhs as AddPeano<Rhs>>::Output>, so <Lhs as AddPeano<Rhs>>::Output must still impl Peano, but the compiler is able to infer that it does.

Similarly, here is another example that I've just stumbled across:

trait Marker {}
struct Bob;
impl Marker for Bob {}

trait DoNothing {
    type Output;
}
impl DoNothing for Bob {
    type Output = Bob;
}
struct Dude<M: Marker>(M);

// Conflicting implementation with this line uncommented,
// Marker not implemented for <Bob as DoNothing>::Output error with it commented
impl Marker for <Bob as DoNothing>::Output {}

const b: Dude<<Bob as DoNothing>::Output> = Dude(Bob);

fn main() {
}

The compiler is able to check that <Bob as DoNothing>::Output implements Marker, but it is seemingly unable to infer it.

@arielb1
Copy link
Contributor

arielb1 commented Jun 16, 2015

@paholg

The where clause is supposed to always be needed. It isn't needed when you don't have a method because of a bug.

Your last example is another (separate) bug.

@jroesch
Copy link
Member

jroesch commented Jun 23, 2015

The final example no longer compiles. When you write programs in the type system the type checker has to execute said programs via type checking so it is expected that it would impact compilation performance. Overall we have a lot of room for optimization in the where clause implementation but I'm not sure if this issue has a directly actionable solution.

@llogiq
Copy link
Contributor

llogiq commented Oct 8, 2015

This is really hurting us freaks who try to implement any type-level shenanigans – I believe this also affects compile times for others in the wild.

I'm not sure if this issue has a directly actionable solution.

Currently having those where clauses in the code means compilation time is growing exponentially with the number of types used. I'm not completely privvy to the details of the type checker implementation, but I'm quite certain the search space could use a good bit of pruning.

@DemiMarie
Copy link
Contributor

If Rust is anything like ML, then typechecking is EXP-complete and provably intractable in the worst case. However, this involves cases where types turn out to be exponentially large and so probably does not apply here.

@jonas-schievink
Copy link
Contributor

Adding the same where-clause to the AddPeano example reproduces the same behaviour. However, if the where-clause is something like where Lhs: Peano, Rhs: Peano, Succ<Lhs>: Peano, Succ<Rhs>: Peano (ie no projections), it still typechecks fast. Maybe #20304 would solve this?

@brson brson added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Mar 23, 2017
@nikomatsakis nikomatsakis added the A-trait-system Area: Trait system label Mar 23, 2017
@brson
Copy link
Contributor

brson commented Mar 23, 2017

This hasn't been updated in a long time. Anybody have new numbers?

@brson brson added the P-low Low priority label Mar 23, 2017
@paholg
Copy link
Author

paholg commented Mar 24, 2017

It seems fixed; the example that used to timeout and gobble ram now has no problems compiling.

@paholg paholg closed this as completed Mar 24, 2017
@fmease fmease added A-trait-system Area: Trait system and removed A-trait-system Area: Trait system labels Dec 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-trait-system Area: Trait system I-slow Issue: Problems and improvements with respect to performance of generated code. P-low Low priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants