scala / bug

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

VerifyError: Inconsistent stackmap frames at branch target #12993

Open danissimo opened 1 month ago

danissimo commented 1 month ago

Reproduction steps

Java: Oracle HotSpot 17.0.6+9-LTS-190, OpenJDK Temurin-17.0.8+7. Scala versions: 2.13.10, 2.13.14.

I faced the behaviour using spray json ("io.spray" %% "spray-json" % "1.3.6"), and I couldn't minimise the snippet to clean scala, but the hint to the nature of the bug is the two workarounds at the bottom of this report.

import spray.json.DefaultJsonProtocol._
import spray.json._

object BadScalac {
  def main(args: Array[String]): Unit = {
    val msg = (
      try Map("block" -> "try").toJson
      catch { case _: Throwable => Map("block" -> "catch").toJson }
    ).prettyPrint + "\n"
    println(msg)
  }
}

Problem

It gets compiled successfully, but when I run I get

Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 152
Exception Details:
  Location:
    BadScalac$.main([Ljava/lang/String;)V @149: goto
  Reason:
    Current frame's stack size doesn't match stackmap.
  Current Frame:
    bci: @149
    flags: { }
    locals: { 'BadScalac$', '[Ljava/lang/String;' }
    stack: { 'spray/json/JsValue' }
  Stackmap Frame:
    bci: @152
    flags: { }
    locals: { 'BadScalac$', '[Ljava/lang/String;' }
    stack: { 'java/lang/Object', 'spray/json/JsValue' }
  Bytecode:
    0000000: bb00 1f59 1220 b700 23b2 0028 b200 2db6
    0000010: 0031 b200 3604 bd00 3859 03b2 003b b200
    0000020: 2d12 3db6 0041 1243 b600 4753 c000 49b6
    0000030: 004d b600 53b6 0057 b200 5cb2 005c b600
    0000040: 60b2 005c b600 60b6 0064 b600 6aa7 004b
    0000050: 57b2 0028 b200 2db6 0031 b200 3604 bd00
    0000060: 3859 03b2 003b b200 2d12 3db6 0041 126c
    0000070: b600 4753 c000 49b6 004d b600 53b6 0057
    0000080: b200 5cb2 005c b600 60b2 005c b600 60b6
    0000090: 0064 b600 6aa7 0003 b600 72b6 0076 1278
    00000a0: b600 76b6 007b 4db2 002d 2cb6 007f b1  
  Exception Handler Table:
    bci [9, 77] => handler: 80
  Stackmap Table:
    same_locals_1_stack_item_extended(@80,Object[#29])
    full_frame(@152,{Object[#2],Object[#132]},{Object[#4],Object[#110]})

    at BadScalac.main(BadScalac.scala)

But it goes away when I rewrite the source code as

import spray.json.DefaultJsonProtocol._
import spray.json._

object BadScalac extends App {
  val msg = (
    try Map("block" -> "try").toJson
    catch { case _: Throwable => Map("block" -> "catch").toJson }
  ).prettyPrint + "\n"
  println(msg)
}

or even when I drop the last addition of the "\n" string:

object BadScalac {
  def main(args: Array[String]): Unit = {
    val msg = (
      try Map("block" -> "try").toJson
      catch { case _: Throwable => Map("block" -> "catch").toJson }
    ).prettyPrint
    println(msg)
  }
}

The both snippets above yield

{
  "block": "try"
}

with and without the last newline respectively.

som-snytt commented 1 month ago

String concat is handled in the backend. Variants that induce Uncurry to lift the try will work.

Other superficially similar ops such as * 2 will work.

Welcome to Scala 2.13.14 (OpenJDK 64-Bit Server VM, Java 21.0.2).
Type in expressions for evaluation. Or try :help.

scala> def f() = (try "try" catch { case _: Throwable => "catch" }).toString + "nope"
java.lang.VerifyError: Inconsistent stackmap frames at branch target 20
Exception Details:
  Location:
    f()Ljava/lang/String; @17: goto
  Reason:
    Current frame's stack size doesn't match stackmap.
  Current Frame:
    bci: @17
    flags: { }
    locals: { '$iw' }
    stack: { 'java/lang/String' }
  Stackmap Frame:
    bci: @20
    flags: { }
    locals: { '$iw' }
    stack: { 'java/lang/Object', 'java/lang/String' }
  Bytecode:
    0000000: bb00 1259 1213 b700 1712 19a7 0009 5712
    0000010: 1ba7 0003 b600 20b6 0024 1226 b600 24b6
    0000020: 0027 b0
  Exception Handler Table:
    bci [9, 11] => handler: 14
  Stackmap Table:
    same_locals_1_stack_item_frame(@14,Object[#16])
    full_frame(@20,{Object[#2]},{Object[#4],Object[#29]})

  ... 29 elided

scala>
som-snytt commented 1 month ago

just for fun

scala> object X { def f() = (try "try" catch (_ => "catch")).toString + "nope" }
-- [E008] Not Found Error: ---------------------------------------------------------------------------------------------
1 |object X { def f() = (try "try" catch (_ => "catch")).toString + "nope" }
  |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |           value toString is not a member of ?{ toString: ? }, but could be made available as an extension method.
  |
  |           The following import might fix the problem:
  |
  |             import scala.reflect.Selectable.reflectiveSelectable
  |
1 error found
sjrd commented 1 month ago

I guess we could fix this by backporting https://github.com/scala/scala3/pull/18619