Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getClass on Unit not erased properly #5568

Closed
scabug opened this issue Mar 13, 2012 · 7 comments
Closed

getClass on Unit not erased properly #5568

scabug opened this issue Mar 13, 2012 · 7 comments

Comments

@scabug
Copy link

@scabug scabug commented Mar 13, 2012

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)
@scabug
Copy link
Author

@scabug scabug commented Mar 13, 2012

@scabug
Copy link
Author

@scabug scabug commented May 4, 2012

@magarciaEPFL said:
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())
  }
}

@scabug
Copy link
Author

@scabug scabug commented May 4, 2012

@magarciaEPFL said (edited on May 4, 2012 4:47:12 PM UTC):
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

@scabug
Copy link
Author

@scabug scabug commented Oct 4, 2012

@som-snytt said (edited on Oct 4, 2012 6:11:39 AM UTC):
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))
  }
}

@scabug
Copy link
Author

@scabug scabug commented Jan 14, 2013

@JamesIry said:
Can reproduce also with (5.asInstanceOf[AnyRef with Int]).getClass

@scabug
Copy link
Author

@scabug scabug commented Jan 15, 2013

@JamesIry said:
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
}

@scabug
Copy link
Author

@scabug scabug commented Jan 15, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant