scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.89k stars 1.06k forks source link

unhandled exception while running prepjsinterop #21302

Open reid-spencer opened 4 months ago

reid-spencer commented 4 months ago

Compiler version

Code base doesn't compile prior to 3.4.1 but issue happens in 3.4.1 & 3.4.2

Minimized code

A GitHub repository has been created to replicate this issue. The README.md file provides the instructions on how to reproduce using that repository.

The repository is here: https://github.com/ossuminc/scala-issue-21302

The minimal scala code:

/*
 * Copyright 2024 Ossum, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

package com.ossuminc.issue

import scala.collection.mutable
import scala.scalajs.js.annotation.*

/** An object for distinguishing several functions as ways to fold the model */
@JSExportTopLevel("Folding")
object Folding {

  trait RiddlValue {
    def isContainer: Boolean
    def isEmpty: Boolean
    def nonEmpty: Boolean = !isEmpty
    def isAnonymous: Boolean = false
  }

  private type Contents[+CV <: RiddlValue] = Seq[CV]

  sealed trait Container[+CV <: RiddlValue] extends RiddlValue {
    def contents: Contents[CV]
    def isContainer: Boolean = true
  }

  private type SimpleDispatch[S, V <: RiddlValue] = (Container[V], V, S) => S

  @JSExport
  def foldEachDefinition[S, V <: RiddlValue](
    parent: Container[V],
    child: V,
    state: S
  )(f: SimpleDispatch[S, V]): S = {
    child match {
      case value: V if value.isContainer && value.nonEmpty =>
        val result = f(parent, child, state)
        val container = value.asInstanceOf[Container[V]]
        container.contents.foldLeft(result) { case (next, child) =>
          foldEachDefinition[S, V](container, child, next)(f)
        }
      case value: V => f(parent, value, state)
    }
  }

  @JSExport final def foldLeftWithStack[S, CT <: RiddlValue](
    value: S,
    top: Container[CT],
    parents: mutable.Stack[Container[CT]] = mutable.Stack.empty[Container[CT]]
  )(f: (S, CT | Container[CT], Seq[Container[CT]]) => S = { (x: S, y: CT, z: Seq[Container[CT]]) => x }): S = {
    val initial = f(value, top, parents.toSeq)
    if !top.isAnonymous then parents.push(top)
    try {
      top.contents.foldLeft(initial) { (next, value) =>
        value match {
          case c: Container[CT] @unchecked if c.nonEmpty => foldLeftWithStack(next, c, parents)(f)
          case v: RiddlValue                             => f(next, v, parents.toSeq)
        }
      }
    } finally {
      if !top.isAnonymous then parents.pop()
    }
  }
}

Output

unhandled exception while running prepjsinterop on /Users/reid/Code/ossuminc/riddl/language/shared/src/main/scala/com/ossuminc/riddl/language/Folding.scala

  An unhandled exception was thrown in the compiler.
  Please file a crash report here:
  https://github.com/scala/scala3/issues/new/choose
  For non-enriched exceptions, compile with -Yno-enrich-error-messages.

     while compiling: /Users/reid/Code/ossuminc/riddl/language/shared/src/main/scala/com/ossuminc/riddl/language/Folding.scala
        during phase: prepjsinterop
                mode: Mode(ImplicitsEnabled)
     library version: version 2.13.12
    compiler version: version 3.4.2
            settings: -Werror true -bootclasspath /Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_3/3.4.2/scala3-library_3-3.4.2.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.12/scala-library-2.13.12.jar -classpath /Users/reid/Code/ossuminc/riddl/language/js/target/scala-3.4.2/classes:/Users/reid/Code/ossuminc/riddl/utils/js/target/scala-3.4.2/classes:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala3-library_sjs1_3/3.4.2/scala3-library_sjs1_3-3.4.2.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-js/scalajs-library_2.13/1.16.0/scalajs-library_2.13-1.16.0.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/fastparse_sjs1_3/3.1.1/fastparse_sjs1_3-3.1.1.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-js/scalajs-dom_sjs1_3/2.8.0/scalajs-dom_sjs1_3-2.8.0.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-js/scalajs-javalib/1.16.0/scalajs-javalib-1.16.0.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-js/scalajs-scalalib_2.13/2.13.13%2B1.16.0/scalajs-scalalib_2.13-2.13.13%2B1.16.0.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/sourcecode_sjs1_3/0.4.0/sourcecode_sjs1_3-0.4.0.jar:/Users/reid/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/geny_sjs1_3/1.1.0/geny_sjs1_3-1.1.0.jar -d /Users/reid/Code/ossuminc/riddl/language/js/target/scala-3.4.2/classes -deprecation true -explain true -explain-cyclic true -explain-types true -feature true -new-syntax true -nowarn true -pagewidth 120 -scalajs true

[error] ## Exception when compiling 33 sources to /Users/reid/Code/ossuminc/riddl/language/js/target/scala-3.4.2/classes
[error] java.lang.AssertionError: assertion failed: Cannot find default getter for param 4 of method foldLeftWithStack
[error] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[error] dotty.tools.dotc.transform.sjs.PrepJSExports$.genExportDefaultGetter(PrepJSExports.scala:411)
[error] dotty.tools.dotc.transform.sjs.PrepJSExports$.$anonfun$9(PrepJSExports.scala:398)
[error] scala.collection.Iterator$$anon$9.next(Iterator.scala:584)
[error] scala.collection.immutable.List.prependedAll(List.scala:153)
[error] scala.collection.immutable.List$.from(List.scala:684)
[error] scala.collection.immutable.List$.from(List.scala:681)
[error] scala.collection.IterableOps$WithFilter.map(Iterable.scala:898)
[error] dotty.tools.dotc.transform.sjs.PrepJSExports$.genExportDefs(PrepJSExports.scala:399)
[error] dotty.tools.dotc.transform.sjs.PrepJSExports$.genExportMember$$anonfun$1(PrepJSExports.scala:98)
[error] scala.collection.immutable.List.flatMap(List.scala:293)
[error] dotty.tools.dotc.transform.sjs.PrepJSExports$.genExportMember(PrepJSExports.scala:98)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transformMemberDef(PrepJSInterop.scala:175)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transform(PrepJSInterop.scala:128)
[error] dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1254)
[error] dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1254)
[error] dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1256)
[error] dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:47)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transformTemplate(PrepJSInterop.scala:222)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transform(PrepJSInterop.scala:129)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1602)
[error] dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:40)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transformScalaClassDef$$anonfun$2(PrepJSInterop.scala:216)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.enterOwner(PrepJSInterop.scala:89)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transformScalaClassDef(PrepJSInterop.scala:217)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transformMemberDef(PrepJSInterop.scala:168)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transform(PrepJSInterop.scala:128)
[error] dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.loop$2(tpd.scala:1254)
[error] dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1254)
[error] dotty.tools.dotc.ast.tpd$TreeMapWithPreciseStatContexts.transformStats(tpd.scala:1256)
[error] dotty.tools.dotc.ast.Trees$Instance$TreeMap.transform(Trees.scala:1613)
[error] dotty.tools.dotc.transform.MacroTransform$Transformer.transform(MacroTransform.scala:40)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transformStatOrExpr(PrepJSInterop.scala:402)
[error] dotty.tools.dotc.transform.sjs.PrepJSInterop$ScalaJSPrepJSInteropTransformer.transform(PrepJSInterop.scala:130)
[error] dotty.tools.dotc.transform.MacroTransform.run(MacroTransform.scala:18)
[error] dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:354)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[error] scala.collection.immutable.List.foreach(List.scala:333)
[error] dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:360)
[error] dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:315)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
[error] scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
[error] scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
[error] dotty.tools.dotc.Run.runPhases$1(Run.scala:337)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:350)
[error] dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:360)
[error] dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69)
[error] dotty.tools.dotc.Run.compileUnits(Run.scala:360)
[error] dotty.tools.dotc.Run.compileSources(Run.scala:261)
[error] dotty.tools.dotc.Run.compile(Run.scala:246)
[error] dotty.tools.dotc.Driver.doCompile(Driver.scala:37)
[error] dotty.tools.xsbt.CompilerBridgeDriver.run(CompilerBridgeDriver.java:141)
[error] dotty.tools.xsbt.CompilerBridge.run(CompilerBridge.java:22)
[error] sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$7(MixedAnalyzingCompiler.scala:193)
[error] scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
[error] sbt.internal.inc.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:248)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4(MixedAnalyzingCompiler.scala:183)
[error] sbt.internal.inc.MixedAnalyzingCompiler.$anonfun$compile$4$adapted(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.JarUtils$.withPreviousJar(JarUtils.scala:239)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:163)
[error] sbt.internal.inc.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:211)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileInternal$1$adapted(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5(Incremental.scala:180)
[error] sbt.internal.inc.Incremental$.$anonfun$apply$5$adapted(Incremental.scala:178)
[error] sbt.internal.inc.Incremental$$anon$2.run(Incremental.scala:464)
[error] sbt.internal.inc.IncrementalCommon$CycleState.next(IncrementalCommon.scala:116)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:56)
[error] sbt.internal.inc.IncrementalCommon$$anon$1.next(IncrementalCommon.scala:52)
[error] sbt.internal.inc.IncrementalCommon.cycle(IncrementalCommon.scala:263)
[error] sbt.internal.inc.Incremental$.$anonfun$incrementalCompile$8(Incremental.scala:419)
[error] sbt.internal.inc.Incremental$.withClassfileManager(Incremental.scala:506)
[error] sbt.internal.inc.Incremental$.incrementalCompile(Incremental.scala:406)
[error] sbt.internal.inc.Incremental$.apply(Incremental.scala:172)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileInternal(IncrementalCompilerImpl.scala:534)
[error] sbt.internal.inc.IncrementalCompilerImpl.$anonfun$compileIncrementally$1(IncrementalCompilerImpl.scala:488)
[error] sbt.internal.inc.IncrementalCompilerImpl.handleCompilationError(IncrementalCompilerImpl.scala:332)
[error] sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally(IncrementalCompilerImpl.scala:425)
[error] sbt.internal.inc.IncrementalCompilerImpl.compile(IncrementalCompilerImpl.scala:137)
[error] sbt.Defaults$.compileIncrementalTaskImpl(Defaults.scala:2371)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$2(Defaults.scala:2321)
[error] sbt.internal.server.BspCompileTask$.$anonfun$compute$1(BspCompileTask.scala:31)
[error] sbt.internal.io.Retry$.apply(Retry.scala:47)
[error] sbt.internal.io.Retry$.apply(Retry.scala:29)
[error] sbt.internal.io.Retry$.apply(Retry.scala:24)
[error] sbt.internal.server.BspCompileTask$.compute(BspCompileTask.scala:31)
[error] sbt.Defaults$.$anonfun$compileIncrementalTask$1(Defaults.scala:2319)
[error] scala.Function1.$anonfun$compose$1(Function1.scala:49)
[error] sbt.internal.util.$tilde$greater.$anonfun$$u2219$1(TypeFunctions.scala:63)
[error] sbt.std.Transform$$anon$4.work(Transform.scala:69)
[error] sbt.Execute.$anonfun$submit$2(Execute.scala:283)
[error] sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
[error] sbt.Execute.work(Execute.scala:292)
[error] sbt.Execute.$anonfun$submit$1(Execute.scala:283)
[error] sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
[error] sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
[error] java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
[error] java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
[error] java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
[error] java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
[error] java.base/java.lang.Thread.run(Thread.java:1583)

Expectation

This exception does not occur during compilation full Linking of Javascript completes

Other Notes

This problem does not happen when compiling for the JVM and seems to have something to do with recursing on a function that has currying, like this:

  @JSExport final def foldLeftWithStack[S, CT <: RiddlValue](
    value: S,
    top: Container[CT],
    parents: mutable.Stack[Container[CT]] = mutable.Stack.empty[Container[CT]]
  )(f: (S, CT | Container[CT], Seq[Container[CT]]) => S = { (x: S, y: CT, z: Seq[Container[CT]]) => x }): S = {
    val initial = f(value, top, parents.toSeq)
    if !top.isAnonymous then parents.push(top)
    try {
      top.contents.foldLeft(initial) { (next, value) =>
        value match {
          case c: Container[CT] @unchecked if c.nonEmpty => foldLeftWithStack(next, c, parents)(f)
          case v: RiddlValue                             => f(next, v, parents.toSeq)
        }
      }
    } finally {
      if !top.isAnonymous then parents.pop()
    }
  }
reid-spencer commented 4 months ago

I found a workaround to this problem: remove the default argument values from both the parents and f parameters. This effectively permits me to keep working and might be a hint as to the actual problem. Hope this makes it easier to find.