typelevel / twiddles

Micro-library for building effectful protocols

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Compilation hangs on Scala 2 when specifying types for fairly long twiddles

francesconero opened this issue · comments

Hello!

Thanks again @mpilquist for the 0.6.2 Skunk release. Unfortunately we found another blocker trying to complete our migration. This time there seems to be a problem when the compiler is forced to check a subtype relation for a Twiddle (in Skunk, this happens often, for example when passing data to queries or commands after inferring the Encoder type from the sql macro).

Here's a minimal example that takes about 30 seconds to compile on a fairly recent laptop:

object Debug {
  import org.typelevel.twiddles._
  
  val inferred =
    1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *:
    1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *:
    1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *: 1 *:
    1 *: 1 *: 1 *: 1 *: 1 *: 1 *:
        EmptyTuple
  
  type Expected = 
    Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *:
    Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *:
    Int *: Int *: Int *: Int *: Int *: Int *: Int *: Int *:
    Int *: Int *: Int *: Int *: Int *: Int *:
        EmptyTuple
  
  inferred: Expected
}

Removing the line inferred: Expected reduces the compile time to under a second, indicating that the issue isn't with the construction of the Twiddle or the Expected type per se.

Anything more than 30 elements in a twiddle, and the compilation times explode.

Here is the compilation profile for the snippet:

*** Cumulative timers for phases
#total compile time           : 1 spans, ()35254.291ms
  parser                      : 1 spans, ()2.018ms (0.0%)
  bm4-parser                  : 1 spans, ()11.805ms (0.0%)
  bm4-parser2                 : 1 spans, ()2.683ms (0.0%)
  kind-projector              : 1 spans, ()3.806ms (0.0%)
  namer                       : 1 spans, ()213.77ms (0.6%)
  packageobjects              : 1 spans, ()14.133ms (0.0%)
  typer                       : 1 spans, ()34539.101ms (98.0%) <------ typechecking is the problem
  bm4-typer                   : 1 spans, ()4.953ms (0.0%)
  superaccessors              : 1 spans, ()0.93ms (0.0%)
  extmethods                  : 1 spans, ()0.186ms (0.0%)
  pickler                     : 1 spans, ()0.299ms (0.0%)
  xsbt-api                    : 1 spans, ()3.484ms (0.0%)
  xsbt-dependency             : 1 spans, ()15.01ms (0.0%)
  refchecks                   : 1 spans, ()12.612ms (0.0%)
  patmat                      : 1 spans, ()0.202ms (0.0%)
  uncurry                     : 1 spans, ()1.76ms (0.0%)
  fields                      : 1 spans, ()0.377ms (0.0%)
  tailcalls                   : 1 spans, ()0.192ms (0.0%)
  specialize                  : 1 spans, ()33.64ms (0.1%)
  explicitouter               : 1 spans, ()0.511ms (0.0%)
  erasure                     : 1 spans, ()4.071ms (0.0%)
  posterasure                 : 1 spans, ()0.405ms (0.0%)
  lambdalift                  : 1 spans, ()0.598ms (0.0%)
  constructors                : 1 spans, ()1.067ms (0.0%)
  flatten                     : 1 spans, ()0.182ms (0.0%)
  mixin                       : 1 spans, ()0.431ms (0.0%)
  cleanup                     : 1 spans, ()0.862ms (0.0%)
  delambdafy                  : 1 spans, ()0.332ms (0.0%)
  jvm                         : 1 spans, ()27.799ms (0.1%)
  scalacenter-profiling       : 1 spans, ()267.273ms (0.8%)
  xsbt-analyzer               : 1 spans, ()7.995ms (0.0%)

and details for the typer phase:

*** Cumulative statistics at phase typer
  #plausibly compatible       : 30 (100.0%)
  #typed                      : 30 (100.0%)
#class symbols                : 10294
#typechecked selections       : 159
#created tree nodes           : 1996
#created scopes               : 750
  #implicit inscope hits      : 30 (100.0%)
#implicit searches            : 30
  #plausibly compatible       : 30 (100.0%)
  #matching                   : 30 (100.0%)
  #typed                      : 30 (100.0%)
  #found                      : 30 (100.0%)
  #implicit inscope hits      : 30 (100.0%)
time spent in scope population: 122 spans, ()0.012ms
#findMember ops               : 2181
  of which not found          : 631 (28.9%)
  of which multiple overloaded: 2 (0.1%)
  of which in implicit        : 1069 (49.0%)
#subtype ops                  : 1236
  of which in implicit        : 510 (41.3%)
#base type seqs               : 204
avg base type seq length      : 4.2
  of which for compound types : 30 (14.7%)
  of which for typerefs       : 173 (84.8%)
#type symbols                 : 19269
#unique types                 : 3221
#typechecked identifiers      : 189
  #found                      : 30 (100.0%)
#symbols                      : 32117
time spent typechecking       : 1 spans, ()34539.089ms
time spent in lubs            : 0 spans, ()0.0ms (0.0%) aggregate, 0.0ms (0.0%) specific
time spent in <:<             : 1236 spans, ()34447.786ms (99.7%) aggregate, 34447.678ms (99.7%) specific <----- subtyping check (?)
time spent in findmember      : 2181 spans, ()0.935ms (0.0%) aggregate, 0.935ms (0.0%) specific
time spent in findmembers     : 0 spans, ()0.0ms (0.0%) aggregate, 0.0ms (0.0%) specific
time spent in asSeenFrom      : 4797 spans, ()25.053ms (0.1%) aggregate, 24.847ms (0.1%) specific
time spent in baseTypeSeq     : 203 spans, ()1.032ms (0.0%) aggregate, 0.714ms (0.0%) specific
time spent in baseClasses     : 93 spans, ()2.786ms (0.0%) aggregate, 2.393ms (0.0%) specific
time classfilereading         : 114 spans, ()55.341ms (0.2%)
time spent in failed          : 0 spans, ()0.0ms (0.0%)
  failed apply                : 0 spans, ()0.0ms (0.0%)
  failed op=                  : 0 spans, ()0.0ms (0.0%)
time spent ref scanning       : 0 spans, ()0.0ms (0.0%)
time spent in implicits       : 30 spans, ()60.276ms (0.2%)
  successful in scope         : 30 spans, ()49.516ms (0.1%)
  failed in scope             : 0 spans, ()0.0ms (0.0%)
  successful of type          : 0 spans, ()0.0ms (0.0%)
  failed of type              : 0 spans, ()0.0ms (0.0%)
  assembling parts            : 0 spans, ()0.0ms (0.0%)
  matchesPT                   : 60 spans, ()0.224ms (0.0%)
time spent in macroExpand     : 0 spans, ()0.0ms (0.0%)
#sametype ops                 : 536870914
#all lubs/glbs                : 60
#typechecked applications     : 91
  #matching                   : 30 (100.0%)

Interestingly, I found that specifying the type variances in the type alias *: for Scala 2 exactly as done in Shapeless (so type *:[+A, +B <: Tuple] = ::[A, B]) eliminates this problem. This observation is intriguing since, in theory, specifying variances in a type alias should be functionally equivalent to not specifying them.

I'll open a PR shortly with this solution, even though I'll admit I'm not 100% sure of why it seems to solve the problem. In the meantime any insights you might have would be incredibly valuable.

Thanks again!