There are two factors here, which we've spotted combined in the wild:
- we hoist all
type X: ...; bounds to the trait-level, i.e.:
trait Trait {
type X: A + B;
type Y: C + D;
}
is sugar for:
trait Trait
where
Self::X: A,
Self::X: B,
Self::Y: C,
Self::Y: D,
{
type X;
type Y;
}
- I doubt we can do much about this, perhaps group/index the bounds?
- lookup of
<_ as Foo>::X: Bar in Foo's where clauses appears to be quadratic
- this wouldn't have a noticeable impact without 1.
- probably easier to fix, I would've assumed it was already linear
And here's our stress test - apologies for the macro, but it takes a lot to push it into multiple seconds (the version in the wild had more sophisticated, and actually useful, macros):
macro_rules! stress {
(type $name:ident: $($i:expr,)*) => {
type $name: $(From<[(); $i]> + Into<[(); $i]> +)*;
};
(fn $($underscore:tt)*) => {
const _: () = {
$({fn _f<T: Trait>() {
#[derive(Copy, Clone)]
struct Foo<X>(X);
let $underscore = Foo(T::X::default()).clone();
}})*
};
};
}
trait Trait {
type X: Copy + Default;
// The bounds would normally be on many separate
// associated types, but that makes no difference.
stress!(type _Unused:
00, 01, 02, 03, 04, 05, 06, 07, 08, 09,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
// Uncomment to (almost) quadruple compile time:
//20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
//30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
// Add more to raise the total time to minutes.
);
}
stress!(fn
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
);
Using -Zself-profile and summarize, we get this (times are total "self" times):
| Query |
20 (00-19) |
40 (00-39) |
Slowdown |
type_op_prove_predicate |
2.52s |
9.17s |
3.64x |
typeck_tables_of |
1.78s |
5.83s |
3.28x |
check_item_well_formed |
1.43s |
4.47s |
3.13x |
cc @rust-lang/wg-traits
There are two factors here, which we've spotted combined in the wild:
type X: ...;bounds to the trait-level, i.e.:<_ as Foo>::X: BarinFoo'swhereclauses appears to be quadraticAnd here's our stress test - apologies for the macro, but it takes a lot to push it into multiple seconds (the version in the wild had more sophisticated, and actually useful, macros):
Using
-Zself-profileandsummarize, we get this (times are total "self" times):00-19)00-39)type_op_prove_predicatetypeck_tables_ofcheck_item_well_formedcc @rust-lang/wg-traits