Details

      Description

      You shouldn't be able to cast your way to a VerifyError.

      object Test {
        final val UNIT: AnyRef with Unit = ().asInstanceOf[AnyRef with Unit]
        
        def main(args: Array[String]): Unit = {
          println(UNIT.getClass)
        }
      }
      
      % scala Test
      java.lang.VerifyError: (class: Test$, method: main signature: ([Ljava/lang/String;)V) Incompatible object argument for function call
      	at Test.main(a.scala)
      

        Issue Links

          Activity

          Hide
          Miguel Garcia added a comment -

          The optimizer can't optimize code that doesn't typecheck, the JVM can't run code that doesn't typecheck, for example the ICode below

          (the "CALL_METHOD scala.Unit.getClass" expects an Object on the stack but finds nothing (the Predef module instance is there to be consumed by the forthcoming Predef.println invocation)

            def main(args: Array[String] (ARRAY[REF(class String)])): Unit {
            locals: value args
            startBlock: 1
            blocks: [1]
            
            1: 
              5	LOAD_MODULE object Predef
              5	CALL_METHOD scala.Unit.getClass (dynamic)
              5	CALL_METHOD scala.Predef.println (dynamic)
              5	RETURN(UNIT)
              
            }
          

          How does that code find its way till GenICode? Like so:

          After superaccessors:

          [[syntax trees at end of superaccessors]]// Scala source: bt4.scala
          package <empty> {
            object Test extends scala.AnyRef {
              def <init>(): Test.type = {
                Test.super.<init>();
                ()
              };
              final private[this] val UNIT: Unit = ().asInstanceOf[Unit];
              final <stable> <accessor> def UNIT: Unit = Test.this.UNIT;
              def main(args: Array[String]): Unit = scala.this.Predef.println(Test.this.UNIT.getClass())
            }
          }
          

          After erasure:

          [[syntax trees at end of erasure]]// Scala source: bt4.scala
          package <empty> {
            object Test extends Object {
              def <init>(): ... = {
                Test.super.<init>();
                ()
              };
              final private[this] val UNIT: Object = scala.runtime.BoxedUnit.UNIT.$asInstanceOf[Object]();
              final <stable> <accessor> def UNIT(): Object = Test.this.UNIT;
              def main(args: Array[String]): Unit = scala.this.Predef.println(().getClass())
            }
          }
          
          Show
          Miguel Garcia added a comment - The optimizer can't optimize code that doesn't typecheck, the JVM can't run code that doesn't typecheck, for example the ICode below (the " CALL_METHOD scala.Unit.getClass " expects an Object on the stack but finds nothing (the Predef module instance is there to be consumed by the forthcoming Predef.println invocation) def main(args: Array[String] (ARRAY[REF(class String)])): Unit { locals: value args startBlock: 1 blocks: [1] 1: 5 LOAD_MODULE object Predef 5 CALL_METHOD scala.Unit.getClass (dynamic) 5 CALL_METHOD scala.Predef.println (dynamic) 5 RETURN(UNIT) } How does that code find its way till GenICode? Like so: After superaccessors: [[syntax trees at end of superaccessors]]// Scala source: bt4.scala package <empty> { object Test extends scala.AnyRef { def <init>(): Test.type = { Test.super.<init>(); () }; final private[this] val UNIT: Unit = ().asInstanceOf[Unit]; final <stable> <accessor> def UNIT: Unit = Test.this.UNIT; def main(args: Array[String]): Unit = scala.this.Predef.println(Test.this.UNIT.getClass()) } } After erasure: [[syntax trees at end of erasure]]// Scala source: bt4.scala package <empty> { object Test extends Object { def <init>(): ... = { Test.super.<init>(); () }; final private[this] val UNIT: Object = scala.runtime.BoxedUnit.UNIT.$asInstanceOf[Object](); final <stable> <accessor> def UNIT(): Object = Test.this.UNIT; def main(args: Array[String]): Unit = scala.this.Predef.println(().getClass()) } }
          Hide
          Miguel Garcia added a comment - - edited

          In transform.Erasure, the Apply tree "Test.this.UNIT().getClass()" is matched by

            case Apply(fn @ Select(qual, _), Nil) if interceptedMethods(fn.symbol) =>
          

          but then doesn't get into this branch

                // Rewrite 5.getClass to ScalaRunTime.anyValClass(5)
                else if (isPrimitiveValueClass(qual.tpe.typeSymbol))
                  global.typer.typed(
                    gen.mkRuntimeCall(
                      nme.anyValClass,
                      List(qual, typer.resolveErasureTag(tree.pos, qual.tpe.widen, true))))
          

          because the qualifier's tpe reads Test.<refinement>.type

          Show
          Miguel Garcia added a comment - - edited In transform.Erasure , the Apply tree " Test.this.UNIT().getClass() " is matched by case Apply(fn @ Select(qual, _), Nil) if interceptedMethods(fn.symbol) => but then doesn't get into this branch // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) global.typer.typed( gen.mkRuntimeCall( nme.anyValClass, List(qual, typer.resolveErasureTag(tree.pos, qual.tpe.widen, true)))) because the qualifier's tpe reads Test.<refinement>.type
          Hide
          A. P. Marki added a comment - - edited

          I stumbled upon this without trying to do any evil. It looks similar.

          The error is:

          java.lang.VerifyError: (class: fracked/Test$$anonfun$fraction$1, method: applyOrElse signature: (ILscala/Function1;)Ljava/lang/Object;) Expecting to find object/array on stack

          package fracked
          
          object Test {
            //def fraction: PartialFunction[Int, Int] = { case d if d != 0 => 42 / d }
            def fraction: PartialFunction[Int, Int] = { case d if d != 0 => println(d.getClass); 42 / d }
            //def fraction: PartialFunction[Int, Int] = { case d: Int if d != 0 => println(d.getClass); 42 / d } // ok
            //def fraction: PartialFunction[Int, Int] = { case d if d != 0 => println("hello"); 42 / d }
            def main(args: Array[String]) {
              println(fraction(42))
            }
          }
          
          Show
          A. P. Marki added a comment - - edited I stumbled upon this without trying to do any evil. It looks similar. The error is: java.lang.VerifyError: (class: fracked/Test$$anonfun$fraction$1, method: applyOrElse signature: (ILscala/Function1;)Ljava/lang/Object;) Expecting to find object/array on stack package fracked object Test { //def fraction: PartialFunction[Int, Int] = { case d if d != 0 => 42 / d } def fraction: PartialFunction[Int, Int] = { case d if d != 0 => println(d.getClass); 42 / d } //def fraction: PartialFunction[Int, Int] = { case d: Int if d != 0 => println(d.getClass); 42 / d } // ok //def fraction: PartialFunction[Int, Int] = { case d if d != 0 => println("hello"); 42 / d } def main(args: Array[String]) { println(fraction(42)) } }
          Hide
          James Iry added a comment -

          Can reproduce also with (5.asInstanceOf[AnyRef with Int]).getClass

          Show
          James Iry added a comment - Can reproduce also with (5.asInstanceOf [AnyRef with Int] ).getClass
          Hide
          James Iry added a comment -

          Two branches to consider. The first is the one Miguel pointed to

          } else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) {
             // Rewrite 5.getClass to ScalaRunTime.anyValClass(5)
             global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen))))
          }
          

          If we go into that one (by hacking in a bit of force) the code that gets generated is very strange and brittle

          3:	getstatic	#24; //Field scala/runtime/ScalaRunTime$.MODULE$:Lscala/runtime/ScalaRunTime$;
             6:	getstatic	#30; //Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
             9:	getstatic	#35; //Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$;
             12:	ldc	#4; //class java/lang/Object
             14:	invokevirtual	#39; //Method scala/reflect/ClassTag$.apply:(Ljava/lang/Class;)Lscala/reflect/ClassTag;
             17:	invokevirtual	#43; //Method scala/runtime/ScalaRunTime$.anyValClass:(Ljava/lang/Object;Lscala/reflect/ClassTag;)Ljava/lang/Class;
          

          The result is "java.lang.Object"

          If instead we go into the next branch

          } else if (fn.symbol == AnyVal_getClass) {
            tree setSymbol Object_getClass
          

          Then what we get is totally reasonable

             3:	getstatic	#25; //Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
             6:	invokevirtual	#29; //Method java/lang/Object.getClass:()Ljava/lang/Class;
          

          And the result is class scala.runtime.BoxedUnit. Which is nice because that's exactly the same answer as when we do [Unit with AnyRef] instad of [AnyRef with Unit].

          What we're currently doing, though, is falling all the way to the default branch and that's getting us into trouble

          } else {
            tree
          }
          
          Show
          James Iry added a comment - Two branches to consider. The first is the one Miguel pointed to } else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) } If we go into that one (by hacking in a bit of force) the code that gets generated is very strange and brittle 3: getstatic #24; //Field scala/runtime/ScalaRunTime$.MODULE$:Lscala/runtime/ScalaRunTime$; 6: getstatic #30; //Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit; 9: getstatic #35; //Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$; 12: ldc #4; //class java/lang/Object 14: invokevirtual #39; //Method scala/reflect/ClassTag$.apply:(Ljava/lang/Class;)Lscala/reflect/ClassTag; 17: invokevirtual #43; //Method scala/runtime/ScalaRunTime$.anyValClass:(Ljava/lang/Object;Lscala/reflect/ClassTag;)Ljava/lang/Class; The result is "java.lang.Object" If instead we go into the next branch } else if (fn.symbol == AnyVal_getClass) { tree setSymbol Object_getClass Then what we get is totally reasonable 3: getstatic #25; //Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit; 6: invokevirtual #29; //Method java/lang/Object.getClass:()Ljava/lang/Class; And the result is class scala.runtime.BoxedUnit. Which is nice because that's exactly the same answer as when we do [Unit with AnyRef] instad of [AnyRef with Unit] . What we're currently doing, though, is falling all the way to the default branch and that's getting us into trouble } else { tree }
          Show
          James Iry added a comment - https://github.com/scala/scala/pull/1904

            People

            • Assignee:
              James Iry
              Reporter:
              Paul Phillips
            • Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development