Skip to content

Presentation compiler crashes because of default arguments in constructor #13112

@dragos

Description

@dragos

Reproduction steps

Scala version: 2.13.16
Using Metals, trying to enable errors as you type (scalameta/metals#7430)

case class Foo(name: String = "")
object Foo extends Foo("")

Problem

The presentation compiler crashes trying to find default getters in the companion object and failing to do so. This only happens when:

  • there are default arguments
  • the object definition is after the class definition

In my investigations I could find that the trigger is that the presentation compiler tries to call invalidateCaches on the previous Run's symbol, which calls into baseClasses, which forces the old symbol before the ConstructorDefaultsAttachment is attached to the class symbol.

Presentation compiler test:

diff --git forkSrcPrefix/test/files/presentation/case-class-default-params/Test.scala forkDstPrefix/test/files/presentation/case-class-default-params/Test.scala
new file mode 100644
index 0000000000000000000000000000000000000000..f91dc1090ed484a4b9fdda437f0ef736de18ad1a
--- /dev/null
+++ forkDstPrefix/test/files/presentation/case-class-default-params/Test.scala
@@ -0,0 +1,34 @@
+import scala.tools.nsc.interactive.tests.InteractiveTest
+import scala.reflect.internal.util.SourceFile
+import scala.tools.nsc.interactive.Response
+
+object Test extends InteractiveTest {
+  override def execute(): Unit = {
+    val source = loadSourceAndWaitUntilTypechecked("Foo.scala")
+    checkErrors(source)
+  }
+
+  private def loadSourceAndWaitUntilTypechecked(sourceName: String): SourceFile = {
+    val sourceFile = sourceFiles.find(_.file.name == sourceName).get
+    compiler.askToDoFirst(sourceFile)
+    val res = new Response[Unit]
+    compiler.askReload(List(sourceFile), res)
+    res.get
+    askLoadedTyped(sourceFile).get
+    // the second round of type-checking makes it fail
+    compiler.askReload(List(sourceFile), res)
+    res.get
+    askLoadedTyped(sourceFile).get
+
+    sourceFile
+  }
+
+  private def checkErrors(source: SourceFile): Unit = compiler.getUnitOf(source) match {
+    case Some(unit) =>
+      val problems = unit.problems.toList
+      if(problems.isEmpty) reporter.println("Test OK")
+      else problems.foreach(problem => reporter.println(problem.msg))
+
+    case None => reporter.println("No compilation unit found for " + source.file.name)
+  }
+}
diff --git forkSrcPrefix/test/files/presentation/case-class-default-params/src/Foo.scala forkDstPrefix/test/files/presentation/case-class-default-params/src/Foo.scala
new file mode 100644
index 0000000000000000000000000000000000000000..ee1e97716fd4b67101a63f25b427a430cb9f3c4c
--- /dev/null
+++ forkDstPrefix/test/files/presentation/case-class-default-params/src/Foo.scala
@@ -0,0 +1,2 @@
+case class Foo(name: String = "")
+object Foo extends Foo("")

Stack trace:

[Foo.scala]: exception during background compile: java.lang.AssertionError: assertion failed: 
  (object Foo,$lessinit$greater$default$1)
     while compiling: case-class-default-params/src/Foo.scala
        during phase: globalPhase=<no phase>, enteringPhase=namer
     library version: version 2.13.16-20250107-233423-3f6bdae
    compiler version: version 2.13.16-20250107-233423-3f6bdae
  reconstructed args: -usejavacp -Ymacro-expand:discard

  last tree to typer: Ident(Foo)
       tree position: line 2 of case-class-default-params/src/Foo.scala
            tree tpe: Foo
              symbol: case class Foo
   symbol definition: case class Foo extends Product with Serializable (a ClassSymbol)
      symbol package: <empty>
       symbol owners: class Foo
           call site: <none> in <none>

== Source file context for tree position ==

     1 case class Foo(name: String = "")
     2 object Foo extends Foo("")
     3 
java.lang.AssertionError: assertion failed: 
  (object Foo,$lessinit$greater$default$1)
     while compiling: case-class-default-params/src/Foo.scala
        during phase: globalPhase=<no phase>, enteringPhase=namer
     library version: version 2.13.16-20250107-233423-3f6bdae
    compiler version: version 2.13.16-20250107-233423-3f6bdae
  reconstructed args: -usejavacp -Ymacro-expand:discard

  last tree to typer: Ident(Foo)
       tree position: line 2 of case-class-default-params/src/Foo.scala
            tree tpe: Foo
              symbol: case class Foo
   symbol definition: case class Foo extends Product with Serializable (a ClassSymbol)
      symbol package: <empty>
       symbol owners: class Foo
           call site: <none> in <none>

== Source file context for tree position ==

     1 case class Foo(name: String = "")
     2 object Foo extends Foo("")
     3 
        at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:173)
        at scala.reflect.internal.SymbolTable.assert(SymbolTable.scala:159)
        at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$10(Namers.scala:1616)
        at scala.tools.nsc.typechecker.Namers$Namer$DefaultGetterInCompanion.addGetter(Namers.scala:1673)
        at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$8(Namers.scala:1586)
        at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$8$adapted(Namers.scala:1557)
        at scala.reflect.internal.util.Collections.foreach2(Collections.scala:260)
        at scala.reflect.internal.util.Collections.foreach2$(Collections.scala:256)
        at scala.reflect.internal.SymbolTable.foreach2(SymbolTable.scala:28)
        at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$6(Namers.scala:1557)
        at scala.collection.LinearSeqOps.foldLeft(LinearSeq.scala:183)
        at scala.collection.LinearSeqOps.foldLeft$(LinearSeq.scala:179)
        at scala.collection.immutable.List.foldLeft(List.scala:79)
        at scala.tools.nsc.typechecker.Namers$Namer.addDefaultGetters(Namers.scala:1553)
        at scala.tools.nsc.typechecker.Namers$Namer.methodSig(Namers.scala:1426)
        at scala.tools.nsc.typechecker.Namers$Namer.memberSig(Namers.scala:1929)
        at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1880)
        at scala.tools.nsc.typechecker.Namers$Namer$MonoTypeCompleter.completeImpl(Namers.scala:834)
        at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter.complete(Namers.scala:2077)
        at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter.complete$(Namers.scala:2075)
        at scala.tools.nsc.typechecker.Namers$TypeCompleterBase.complete(Namers.scala:2070)
        at scala.reflect.internal.Symbols$Symbol.completeInfo(Symbols.scala:1583)
        at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1548)
        at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1747)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5916)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:6344)
        at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:6422)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStat$1(Typers.scala:3387)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$10(Typers.scala:3547)
        at scala.collection.immutable.List.loop$3(List.scala:473)
        at scala.collection.immutable.List.mapConserve(List.scala:498)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3547)
        at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:2133)
        at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1971)
        at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:6251)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:6304)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:6344)
        at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:6422)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStat$1(Typers.scala:3387)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$10(Typers.scala:3547)
        at scala.collection.immutable.List.loop$3(List.scala:473)
        at scala.collection.immutable.List.mapConserve(List.scala:498)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3547)
        at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5925)
        at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:6254)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:6304)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:6344)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:6420)
        at scala.tools.nsc.typechecker.Analyzer$typerFactory$TyperPhase.apply(Analyzer.scala:126)
        at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:483)
        at scala.tools.nsc.interactive.Global$TyperRun.$anonfun$applyPhase$1(Global.scala:1370)
        at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
        at scala.reflect.internal.SymbolTable.enteringPhase(SymbolTable.scala:292)
        at scala.tools.nsc.interactive.Global$TyperRun.applyPhase(Global.scala:1370)
        at scala.tools.nsc.interactive.Global$TyperRun.typeCheck(Global.scala:1363)
        at scala.tools.nsc.interactive.Global.typeCheck(Global.scala:681)
        at scala.tools.nsc.interactive.Global.$anonfun$backgroundCompile$7(Global.scala:591)
        at scala.tools.nsc.interactive.Global.$anonfun$backgroundCompile$7$adapted(Global.scala:587)
        at scala.Option.foreach(Option.scala:437)
        at scala.tools.nsc.interactive.Global.$anonfun$backgroundCompile$6(Global.scala:587)
        at scala.tools.nsc.interactive.Global.$anonfun$backgroundCompile$6$adapted(Global.scala:587)
        at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:619)
        at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:617)
        at scala.collection.AbstractIterable.foreach(Iterable.scala:935)
        at scala.collection.IterableOps$WithFilter.foreach(Iterable.scala:905)
        at scala.tools.nsc.interactive.Global.backgroundCompile(Global.scala:587)
        at scala.tools.nsc.interactive.PresentationCompilerThread.run(PresentationCompilerThread.scala:32)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions