bug icon indicating copy to clipboard operation
bug copied to clipboard

Compiler crashes with code involving `return` inside `try`

Open scabug opened this issue 8 years ago • 12 comments

Scala 2.12 (both .0 and .1) compiler crashes when trying to compile my project. Specifically, it throws the following exception:

scala.tools.asm.tree.analysis.AnalyzerException: While processing switchwalker/Inspector.inspect
....
Caused by: scala.tools.asm.tree.analysis.AnalyzerException: Error at instruction 350: Incompatible stack heights
....
Caused by: scala.tools.asm.tree.analysis.AnalyzerException: Incompatible stack heights
....
[error] Error while emitting switchwalker/Inspector
[error] While processing switchwalker/Inspector.inspect

The construct that hurts the compiler seems to look something like this:

def inspect(): Result
{
  var switch: Switch = null
  ....
  try {
  /*** 1 ***/   switch = Switch(...) match {
        case Right(sw) => sw
        case Left(result) => return result
     }//match
     ...
  } catch {
     ....
  /*** 2 ***/ } finally {
      if (switch != null)
         switch.cleanup()
  }//finally

  result
}//inspect


Commenting out the assignment in the point marked /*** 1 / (without touching the following "match" clause) or removing the "finally" clause in the point / 2 ***/ stops the compiler from crashing. But no other changes in the same file (those I've tried) help.

The project compiles without any problems with Scala 2.11. I could not reproduce the crash with the smaller example project, containing the similar constructs. So I supply the full stack trace and the source file that causes the compiler to crash in the attachment. The points in the source file where the changes can help the compiler to survive are also marked /*** 1 / and / 2 ***/.

I also can supply the entire project if required (since it is not secret nor very big), so the crash could be reproduced.

scabug avatar Feb 13 '17 12:02 scabug

Imported From: https://issues.scala-lang.org/browse/SI-10183?orig=1 Reporter: Dmitriy Stepanenko (mpolk) Affected Versions: 2.12.0, 2.12.1 Attachments:

scabug avatar Feb 13 '17 12:02 scabug

@lrytz said: Hi, thanks for the report. I'm trying turn your file into a self-contained example that triggers the crash. In the meantime, you can probably use -opt:l:none as a workaround.

scabug avatar Feb 13 '17 13:02 scabug

@lrytz said (edited on Feb 13, 2017 2:16:21 PM UTC): [~mpolk] I managed to make a self-contained example, a minimized version: https://gist.github.com/lrytz/fb9c38d0454f05ddb34907e4b4cc169c

scabug avatar Feb 13 '17 13:02 scabug

Dmitriy Stepanenko (mpolk) said: Thanks, Lukas. Probably you are right about the minimum self-contained program causing this compiler crash. I've tried a similar program, but without a "foreach" cycle in the middle, which seems to be essential to crash the compiler.

scabug avatar Feb 13 '17 19:02 scabug

@lrytz said (edited on Feb 14, 2017 11:31:46 AM UTC): Minimized:

class C {
  def fin(): Unit = ()
  def m(x: Int) = ()
  def inspect: Unit = {
    try {
      m(if (this == null) return else 1) // jump to finally block, `this` on stack
      return                             // jump to finally block, empty stack
    } finally {
      fin()
    }
  }
}

scabug avatar Feb 14 '17 10:02 scabug

@lrytz said: This looks not easy to fix unfortunately.

For a try-finally, the finally block is emitted in 3 copies

  • once as a handler, if an exception is thrown in the try block (or any catch block)
  • once for ordinary control flow
  • once for return statements within the try block

return statements within the try are replaced by a store (if there's a return value) and a jump to the 3rd finally copy (called "cleanup"). This is handled in genReturn.

The classfile spec requires the stack to be the same (height and types) for all branches to a location. In the example, the at the first return, there's a value on the stack (the receiver of method m), at the second return there isn't. What we should do is insert drop operations until the stack is clear, but we don't keep track of the stack height in our code generator.

In 2.11.x, returns were handled differently: the finally block was inlined into the try block above every return.

Ideas welcome, cc @retronym.

scabug avatar Feb 14 '17 13:02 scabug

@lrytz said: https://github.com/lrytz/scala/tree/t10183

scabug avatar Feb 14 '17 13:02 scabug

For the record, javac duplicates the finalizer like we did in 2.11.

lrytz avatar Oct 20 '17 11:10 lrytz

as of 2.13.10, the error message on Lukas's reproduction is:

error: Error while emitting C
Index 0 out of bounds for length 0

SethTisue avatar Feb 09 '23 01:02 SethTisue

Scala 3.2.2 gives:

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 while running genBCode on rs$line$1
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
	at scala.tools.asm.Frame.merge(Frame.java:1268)

SethTisue avatar Feb 09 '23 01:02 SethTisue

Just wanted to drop here a line in the hope it would help anyone. In my case the code was as follows

❯ scalac -version Scala compiler version 2.13.12 -- Copyright 2002-2023, LAMP/EPFL and Lightbend, Inc.

❯ cat Repro.scala
class Process() {
   def exit() = println("exit")
}

class Actor extends Process {
  override def exit() = {
    try {
      println("try")
    } catch {
      case e: Throwable => 'ignored
    }
  }
}
❯ scalac -d /tmp Repro.scala
error: Error while emitting Actor
Index 0 out of bounds for length 0
1 error

Notice that the return type of the exit in superclass is Unit. While in override the return type from the catch branch is Symbol. The fix for me was to return () from the function.

iilyak avatar Jan 31 '24 18:01 iilyak

The original reproducer in https://github.com/scala/bug/issues/10183#issuecomment-292443914 also return incorrect type (Integer instead of Unit).

iilyak avatar Jan 31 '24 19:01 iilyak

Just realized lrytz's minimization was also on Valentine's Day. SethTisue's followup last year was a week before because he's always busy on Valentine's Day. :heart:

som-snytt avatar Feb 15 '24 04:02 som-snytt