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

ClassFormatError using specialized annotation #7288

Closed
scabug opened this issue Mar 22, 2013 · 14 comments
Closed

ClassFormatError using specialized annotation #7288

scabug opened this issue Mar 22, 2013 · 14 comments
Milestone

Comments

@scabug
Copy link

scabug commented Mar 22, 2013

It seems that the scala compiler is generating multiple equals methods with the same signature. First of all here is a toy code sample that reproduces the issue:

trait TestTrait[@specialized(Double) T] {
    var array: Array[T] = null
}

class MyClass extends TestTrait[Double] {
    array = new Array[Double](0)
}

val tst = new MyClass

This program compiles correctly using the scala compiler, however upon instantiation of MyClass at run time, the class loader throws the following exception:

java.lang.ClassFormatError: Duplicate method name&signature in class file Main$$anon$1$MyClass
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
	at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

Running javap shows the following duplicate method has been generated:

$ javap MyClass.class | sort | uniq -d
output: public void array_$eq(java.lang.Object);

Additionally using JAD to decompile the class shows the source of the two methods:

public void array_$eq(Object x$1)
{
    array = (double[])x$1;
}

public volatile void array_$eq(Object x$1)
{
    array_$eq((double[])x$1);
}

FYI: I don't think this is a blocker, it's my first time filing an issue, and I was unable to change the default priority.

@scabug
Copy link
Author

scabug commented Mar 22, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7288?orig=1
Reporter: Bryan Gilbert (gilbertw1)
Affected Versions: 2.10.0, 2.11.0-M8

@scabug
Copy link
Author

scabug commented Dec 11, 2013

@soc said:
Normally the specializer manages to not create duplicate methods ... could this be an issue where “volatile” causes the methods to appear different to scalac?

@scabug
Copy link
Author

scabug commented Jan 30, 2014

@gzm0 said:
Note that the following tests expose this behavior, but do not test against it:

  • pos/SI-4012-a.scala
  • pos/SI-7638.scala

@scabug
Copy link
Author

scabug commented Mar 29, 2016

@scabug
Copy link
Author

scabug commented Mar 30, 2016

kenji yoshida said (edited on Mar 30, 2016 1:23:07 AM UTC):
minimized

scalaVersion := "2.11.8"
package com.example

case class Foo[F[_], A](step: F[Foo.Step[A]])

object Foo {
  class Step[A]
  object Done {
    def unapply[A](a: Step[A]): Boolean = true
  }
}

object ClassFormatErrorBug {
  implicit class AnyOps[A](actual: A) {
    def mustMatch[B](f: PartialFunction[A, B]): Unit = {
    }
  }

  type Id[+A] = A

  def main(args: Array[String]): Unit = {
    val s = new Foo[Id, String](new Foo.Step[String])

    s.step mustMatch {
      case Foo.Done() => true
    }
  }
}

@scabug
Copy link
Author

scabug commented Mar 30, 2016

@SethTisue said:
offhand, it doesn't look like the same issue, since specialization isn't involved. also, in your case, I don't see the duplicate method in the output of javap, which puzzles me.

also, I'm skeptical that you've really minimized this. occasionally a bug requires this much code to trigger, but it's very rare. so for example, when I pull {{Step}} out to the outer level and remove its type parameters, I still get the same error. nor does the error go away if eliminate {{Foo}}'s companion object and promote {{Done}} to the top level. I suspect there are further such reductions that could result in even more minimized code.

@scabug
Copy link
Author

scabug commented Mar 30, 2016

@VladUreche said (edited on Mar 30, 2016 12:57:58 PM UTC):
The erased signatures of the method applyOrElse and its bridge in class com/example/ClassFormatErrorBug$$anonfun$main$1 really collide:

$ javap -s -private -classpath . 'com/example/ClassFormatErrorBug$$anonfun$main$1'
Compiled from "SI-7288.scala"
public final class com.example.ClassFormatErrorBug$$anonfun$main$1 extends scala.runtime.AbstractPartialFunction<com.example.Foo$Step<java.lang.String, com.example.Foo<java.lang.Object, java.lang.String>>, java.lang.Object> implements scala.Serializable {
  public final <A1 extends com/example/Foo$Step<java/lang/String, com/example/Foo<java/lang/Object, java/lang/String>>, B1 extends java/lang/Object> B1 applyOrElse(A1, scala.Function1<A1, B1>);
    Signature: (Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;

  public final java.lang.Object applyOrElse(java.lang.Object, scala.Function1);
    Signature: (Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;

  ...
}

This has been fixed in miniboxing: miniboxing/miniboxing-plugin#192.

@scabug
Copy link
Author

scabug commented Mar 30, 2016

@retronym said:
Minimized:

package com.example

object ClassFormatErrorBug {
  type Id[A] = A

  def main(args: Array[String]): Unit = {
    new PF1
  }
}

abstract class PF[A, B]{
  def x[A1 <: A](x1: A1): Any
}
class PF1 extends PF[ClassFormatErrorBug.Id[java.lang.String], Any] {
  def x[A1 <: ClassFormatErrorBug.Id[java.lang.String]](x1: A1): Any = null
}

@scabug
Copy link
Author

scabug commented Mar 30, 2016

@SethTisue said (edited on Mar 30, 2016 4:20:34 PM UTC):
alternate minimization:

object SI_7288 {
  type Id[A] = A
  def f[A](pf: PartialFunction[A, Unit]): Unit = ()
  f[Id[Int]] { case _ => }
  def main(args: Array[String]): Unit = ()
}

@scabug
Copy link
Author

scabug commented Mar 30, 2016

@VladUreche said:
Thanks for the minimization Jason (I needed something that did not rely on the library).

Seems miniboxing only removes the redundant bridges generated by itself, not those generated by erasure, so it doesn't fix the problem completely.
In case you want to add the transformation to scalac, the code that removes redundant bridges in miniboxing is this:
https://github.com/miniboxing/miniboxing-plugin/blob/wip/components/plugin/src/miniboxing/plugin/transform/tweakerasure/TweakErasureTreeTransformer.scala#L35-L58

@SethTisue
Copy link
Member

on 2.12.4 my minimization gives

java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    SI_7288$$anonfun$1.applyOrElse(ILscala/Function1;)Ljava/lang/Object; @0: aload_1
  Reason:
    Type integer (current frame, locals[1]) is not assignable to reference type
  Current Frame:
    bci: @0
    flags: { }
    locals: { 'SI_7288$$anonfun$1', integer, 'scala/Function1' }
    stack: { }
  Bytecode:
    0x0000000: 2bb8 001f 3604 b200 254e a700 032d b0
  Stackmap Table:
    append_frame(@13,Object[#33],Integer)

  ... 30 elided

@SethTisue
Copy link
Member

scala/scala#8569 shows that the original report was fixed at least as of 2.12.0, so I'll close the ticket with that milestone

my alternate minimization remains unfixed even in 2.13.1; I've opened a separate ticket on it: #11817

@hrhino
Copy link
Member

hrhino commented Dec 2, 2019

Thanks! I've been taking a look at it because it's not right to leave it here to rot.

@som-snytt
Copy link

s/rot/compost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants