scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

AssertionError in scalac when compiling a case class with -Ytyper-debug enabled #12675

Open noresttherein opened 2 years ago

noresttherein commented 2 years ago

Reproduction steps

Scala version: 2.13.10

case class CaseClass(field :Int)

Problem

When compiling with -Ytyper-debug:

scalac: Error: assertion failed: (CaseClass,case class CaseClass extends scala.Product with scala.Serializable {
  <caseaccessor> <paramaccessor> private[this] val field: Int = _;
  def <init>(field: Int): CaseClass = {
    super.<init>();
    ()
  }
})
java.lang.AssertionError: assertion failed: (CaseClass,case class CaseClass extends scala.Product with scala.Serializable {
  <caseaccessor> <paramaccessor> private[this] val field: Int = _;
  def <init>(field: Int): CaseClass = {
    super.<init>();
    ()
  }
})
    at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:171)
    at scala.tools.nsc.typechecker.TypersTracking$typingStack$.pop(TypersTracking.scala:98)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:6100)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStat$1(Typers.scala:6125)
    at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$8(Typers.scala:3403)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3403)
    at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5640)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5960)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:6047)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$TyperPhase.apply(Analyzer.scala:117)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:467)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$TyperPhase.run(Analyzer.scala:106)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1530)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1514)
    at scala.tools.nsc.Global$Run.compileSources(Global.scala:1506)
    at scala.tools.nsc.Global$Run.compileFiles(Global.scala:1619)
    at xsbt.CachedCompiler0.run(CompilerBridge.scala:163)
    at xsbt.CachedCompiler0.run(CompilerBridge.scala:134)
    at xsbt.CompilerBridge.run(CompilerBridge.scala:39)
    at sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
    at org.jetbrains.jps.incremental.scala.local.IdeaIncrementalCompiler.compile(IdeaIncrementalCompiler.scala:57)
    at org.jetbrains.jps.incremental.scala.local.LocalServer.doCompile(LocalServer.scala:52)
    at org.jetbrains.jps.incremental.scala.local.LocalServer.compile(LocalServer.scala:30)
    at org.jetbrains.jps.incremental.scala.remote.Main$.compileLogic(Main.scala:209)
    at org.jetbrains.jps.incremental.scala.remote.Main$.$anonfun$handleCommand$1(Main.scala:192)
    at org.jetbrains.jps.incremental.scala.remote.Main$.decorated$1(Main.scala:182)
    at org.jetbrains.jps.incremental.scala.remote.Main$.handleCommand(Main.scala:189)
    at org.jetbrains.jps.incremental.scala.remote.Main$.serverLogic(Main.scala:165)
    at org.jetbrains.jps.incremental.scala.remote.Main$.nailMain(Main.scala:105)
    at org.jetbrains.jps.incremental.scala.remote.Main.nailMain(Main.scala)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at com.facebook.nailgun.NGSession.runImpl(NGSession.java:312)
    at com.facebook.nailgun.NGSession.run(NGSession.java:198)

Explain how the above behavior isn't what you expected. Do I have to?

som-snytt commented 2 years ago

Since 2.12.7. -Vtyper in 2.13. It's always been a bit finicky.

Edit: 2.12.7 includes 2.13.0-RC1 backports.

som-snytt commented 2 years ago

By finicky, I mean duplicates https://github.com/scala/bug/issues/12601 which is the same stack trace, but I don't remember the disposition, so let's leave this open until there is some clarity.

noresttherein commented 2 years ago

I don't want to appear entitled, but would it be possible to catch errors in the compiler and provide additional information about the location in the code it was looking at when it was caught? Stack overflows in the compiler can be super tricky to debug, it often takes me up to a week to find the cause, especially when introduced by a larger refactor. As the compiler jumps around quite a bit, not always the last location printed by a debug option gives a good idea about where the problem is, let alone what it is.

For example, I personally use something like:

rethrow { persistence.save(entity) } (s"Failed to save $entity.")

def rethrow[T](block: => T)(msg: => String) :T =
  try { block } catch {
    case e :Throwable =>
        e.addSuppressed(new RethrowContext(msg))
        throw e
}
class RethrowContext(msg: =>String) extends Exception {
    override lazy val getMessage = msg
}

It's not a huge effert to pepper the code with such rethrows, at least in places liable to cause problems - TypeComparers would be my first candidate. This way, an exception printed to the console with any standard formatter will include the information from higher on the stack. It really would be a huge quality of life feature from my point of view.

som-snytt commented 2 years ago

By "larger refactor", I guess you mean that you refactor your code and the (maybe buggy compiler) symptom changes so it's hard to know how to get your code to compile nicely again?

For error context there is https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/Global.scala#L1080

It doesn't always know when to discard error context as it tries a series of adaptations, for example. (Also, not an expert.)

noresttherein commented 2 years ago

I feel really stupid, as I see I actually reported it previously myself and have no recollection of it. I don't know why it didn't show in my search with a stack trace element? I swear I started with it in hope that maybe there is some info on it. I must have searched with the default is:open I guess.

And yes, especially things like adding/removing a type parameter to a whole class hierarchy are risky in my experience. And regarding the error context, isn't the method only called when the compiler actually reports an error, rather than terminates by throwing an Error?

som-snytt commented 2 years ago

and have no recollection of it.

That seems totally normal to me. Possibly, I mean "normalized".

I wonder if there are certain refactorings that play havoc with incremental compilation.

I think abort will try to supplement with typer state; it was originally a simpler try-catch thing, IIRC, but I see now it zig-zags through the reporter for the current run (which does all the buffered messaging and warning config).

On context, there have been efforts around typing, but less around crashing cleanly. Maybe Scala 4 will be the compiler with opinionated crashes.

SethTisue commented 1 year ago

About improving error reporting when the compiler crashes: fwiw, we're trying to improve that in Scala 3 over at https://github.com/lampepfl/dotty/pull/16593 . It's an area where pull requests remain welcome in Scala 2 as well.

SethTisue commented 1 year ago

So, should this one stay open or not? I'm confused by the history here.

som-snytt commented 1 year ago

The linked ticket had 2 cases: the NPE was transferred to zinc; the assert duplicated this ticket.

I vote leave open for either fix the assertion or improve the crash output.