Very slow compile time involving path-dependent types
See original GitHub issueCompiler version
3.1.2
Minimized code
Given the minimized code:
class /[D, T]
class Delegating[D]
type Aux[E] = Container { type Elements = E }
class Container:
type Elements = Delegating[Delegates]
type Delegates
class Resolution[E](value: Aux[E]):
type Type = Aux[E]
The following code compiles almost instantly (which is expected):
def element0: Container { type Delegates = Unit } = ???
def element16(
transmittable0: Resolution[?], transmittable1: Resolution[?],
transmittable2: Resolution[?], transmittable3: Resolution[?],
transmittable4: Resolution[?], transmittable5: Resolution[?],
transmittable6: Resolution[?], transmittable7: Resolution[?],
transmittable8: Resolution[?], transmittable9: Resolution[?],
transmittable10: Resolution[?], transmittable11: Resolution[?],
transmittable12: Resolution[?], transmittable13: Resolution[?],
transmittable14: Resolution[?], transmittable15: Resolution[?])
: Container {
type Delegates =
transmittable0.Type / transmittable1.Type /
transmittable2.Type / transmittable3.Type /
transmittable4.Type / transmittable5.Type /
transmittable6.Type / transmittable7.Type /
transmittable8.Type / transmittable9.Type /
transmittable10.Type / transmittable11.Type /
transmittable12.Type / transmittable13.Type /
transmittable14.Type / transmittable15.Type
} = ???
def test16 =
Resolution(
element16(
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0)))
But the following example, which is just a slightly bigger version of the previous example (22 instead of 16 parameters), takes 10+ seconds to compile on my machine:
def element22(
transmittable0: Resolution[?], transmittable1: Resolution[?],
transmittable2: Resolution[?], transmittable3: Resolution[?],
transmittable4: Resolution[?], transmittable5: Resolution[?],
transmittable6: Resolution[?], transmittable7: Resolution[?],
transmittable8: Resolution[?], transmittable9: Resolution[?],
transmittable10: Resolution[?], transmittable11: Resolution[?],
transmittable12: Resolution[?], transmittable13: Resolution[?],
transmittable14: Resolution[?], transmittable15: Resolution[?],
transmittable16: Resolution[?], transmittable17: Resolution[?],
transmittable18: Resolution[?], transmittable19: Resolution[?],
transmittable20: Resolution[?], transmittable21: Resolution[?])
: Container {
type Delegates =
transmittable0.Type / transmittable1.Type /
transmittable2.Type / transmittable3.Type /
transmittable4.Type / transmittable5.Type /
transmittable6.Type / transmittable7.Type /
transmittable8.Type / transmittable9.Type /
transmittable10.Type / transmittable11.Type /
transmittable12.Type / transmittable13.Type /
transmittable14.Type / transmittable15.Type /
transmittable16.Type / transmittable17.Type /
transmittable18.Type / transmittable19.Type /
transmittable20.Type / transmittable21.Type
} = ???
def test22 =
Resolution(
element22(
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0),
Resolution(element0), Resolution(element0)))
My “real” (not minimized code) has a few more parameters and takes ages to compile (did not finish yet after a several minutes), which makes me think that code in this shape maybe triggers some kind of exponential explosion in the type checker.
Expectation
Scala 2.13 compiles the code instantly. The expectation would be the same for Scala 3.
Note
FYI, I found #14224, which may or may not have the same root cause.
Issue Analytics
- State:
- Created a year ago
- Comments:5 (3 by maintainers)
Top Results From Across the Web
Scala 3: dealing with path dependent types
Types are inferred at compile time, so a.T =:= a1.T is checked at compile time when we can't know yet whether a and...
Read more >Can a dependent type system catch *all* type error...
A language with a very powerful type system can still have a very dumb compiler (although it will generate extremely slow code).
Read more >Path dependent types
Well, actually dependent typing is helpful in solving very down-to-earth problems. I bet that everyone programming in a language with sufficiently expressive ...
Read more >Dependent Types are a Runtime Maybe : r/haskell
Dependent types can actually make a program run faster. (NB: run faster, not compile faster.) This can happen when the types give the...
Read more >Dependent Types and Compile Time Types
Instead of dealing with polymorphism and dependency of types on values directly in the static semantics, C++ adds an extra untyped language ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I checked that indeed every increase in element number by one gives twice as many subtype calls. (How?: I turned the inline value Stats.enabled on, compiled with -Ydetailed-stats and looked at the number of “total subtype” entries for various sizes of element.).
The reason, I believe is this: When testing equality
A =:= B
we testA <:< B
andB <:< A
. The program here has a deeply nested applied type of an invariant type constructor/
. Comparing applied invariant types means testing corresponding type arguments for equality. So at each level we have a branch out factor of 2, for an overall exponential complexity.This helps in the particular test case because then the two types that are compared hash-cons after dealiasing to the same type. But the fix in #15556 is more general.