[SI-7120] java.lang.ClassFormatError: Duplicate method name&signature in class file Created: 12/Feb/13  Updated: 03/Jan/14  Resolved: 23/Feb/13

Status: CLOSED
Project: Scala Programming Language
Component/s: None
Affects Version/s: Scala 2.10.0
Fix Version/s: Scala 2.11.0-M2

Type: Bug Priority: Critical
Reporter: Tomer Gabel Assignee: Jason Zaugg
Resolution: Fixed Votes: 0
Labels: None


After migrating from 2.9.2 to 2.10 my team has encountered a runtime ClassFormatError, which I've managed to reduce to a simple single-file reproduction case which involves a type parameter, type alias and a partial function. The sample is reproduced here (full code as a gist):

case class Container( v: String )
trait Base[ T <: AnyRef ] {
  type UserType = T
	protected def defect: PartialFunction[ UserType, String ]
trait Derived extends Base[ Container ] {
  protected def defect = { case c: Container => c.v.toString }
object Test extends Derived with App {
	println( defect( Container( "8" ) ) )

The generated class file for Derived.defect clearly has duplicate method signatures:

wanamingo:tmp tomer$ scalap -cp . 'com.tomergabel.examples.Derived$$anonfun$defect$1'
package com.tomergabel.examples;
final class Derived$$anonfun$defect$1 extends scala.runtime.AbstractPartialFunction with scala.Serializable {
  def this(com.tomergabel.examples.Derived): scala.Unit;
  def applyOrElse(scala.Any, scala.Function1): scala.Any;
  def isDefinedAt(scala.Any): scala.Boolean;
  def isDefinedAt(com.tomergabel.examples.Container): scala.Boolean;
  def applyOrElse(scala.Any, scala.Function1): scala.Any;
object Derived$$anonfun$defect$1 {
  final val serialVersionUID: scala.Long;

Fortunately this can be worked around by qualifying the derived method with a full return type:

  protected def defect: PartialFunction[ Container, String ] = { case c: Container => c.v.toString }

Comment by Jason Zaugg [ 13/Feb/13 ]

aka "a bridge too far".

[log erasure] computing bridges for anonymous class $anonfun
[log erasure(->explicitouter)] overriding-pairs? method applyOrElse matches method applyOrElse in trait PartialFunction ([A1 <: <empty>.this.Container, B1 >: lang.this.String](<param> <synthetic> <triedcooking> x1: A1, <param> <synthetic> default: scala.this.Function1[A1,B1])B1/class scala.reflect.internal.Types$PolyType vs. [A1 <: <empty>.this.Container, B1 >: lang.this.String](<param> x: A1, <param> default: scala.this.Function1[A1,B1])B1/class scala.reflect.internal.Types$PolyType) == true
[log erasure] generating bridge from method applyOrElse (<method> final override <bridge>): (<param> x: Object, <param> default: Function1)Object in trait PartialFunction to method applyOrElse: (<param> <synthetic> <triedcooking> x1: Container, <param> <synthetic> default: Function1)Object
[log erasure] overriding-pairs? method isDefinedAt matches method isDefinedAt in trait PartialFunction ((<param> <synthetic> <triedcooking> x1: <empty>.this.Container)scala.this.Boolean/class scala.reflect.internal.Types$MethodType vs. (<param> x: <empty>.this.Container)scala.this.Boolean/class scala.reflect.internal.Types$MethodType) == true

Comment by Jason Zaugg [ 13/Feb/13 ]

Regressed with the new encoding of PartialFunctions: 383d28daa16

But it's possible that this just lifted the lid on some existing problem in erasure; I'm trying to track down why x1 in the non-bridge signature below erases to Object, rather than Container.

        final override def applyOrElse(x1: Object, default: Function1): Object = {
        final override <bridge> def applyOrElse(x: Object, default: Function1): Object = $anonfun.this.applyOrElse(x.$asInstanceOf[Container](), default)

Comment by Jason Zaugg [ 13/Feb/13 ]

Yep, it was a problem in erasure.

I think this will fix it:


Checking for collateral damage:


We can't fix this for 2.10.x as changing the erased types risks binary compatibility breakages for ourselves, and downstream libraries that are themselves trying to offer binary compatibility guarantees.

If that build goes well, I'll submit a PR against master.

Comment by Jason Zaugg [ 14/Feb/13 ]


Comment by Ben Hutchison [ 09/Jul/13 ]

I tripped over this issue in a different case. Worked around OK. Including if it helps anyone to workaround until 2.11 is out.

object AmbiguousImplicitExample extends App {
  trait ApproxEq[T] extends (T => ApproxEqOps[T]) {
    //*** triggers the bug, remove to workaround ***
    def apply(v: T): ApproxEqOps[T]
    def approxEq(a: T, b: T): Boolean
  implicit object doubleApprox extends ApproxEq[Double] {
    def approxEq(a: Double, b: Double): Boolean = ???
    def apply(v1: Double): ApproxEqOps[Double] = new ApproxEqOps[Double](v1)
  class ApproxEqOps[T](val value: T) extends AnyVal {
    def ===(other: T)(implicit eq: ApproxEq[T]) = ???

Comment by Adriaan Moors [ 03/Jan/14 ]

Here's another real-world use case, distilled from a Slick 2.0.0-RC1 app:

// derived from a real-world Slick app (support #3035)
// running Test should not throw a java.lang.ClassFormatError: Duplicate method name&signature in class file Test$$anonfun$1 
// workaround: uncomment type arg to list below
class AbstractTable[T] { type TableElementType }
class Table[T] extends AbstractTable[T] { type TableElementType = T }
class Query[E, U]
class TableQuery[E <: AbstractTable[_]] extends Query[E, E#TableElementType]
object Test extends App {
  object MyTable extends TableQuery[Table[Long]]
  def list[R](q: Query[_, R]): List[R] = Nil
  list/*[Long]*/(MyTable) collect { case x => x }

Comment by Adriaan Moors [ 03/Jan/14 ]

Hmm, looks like that last example is a bug in uncurry where signatures are dealiased, but not the type params/value params that constitute them. Found diffing -Yshow-syms.

Comment by Adriaan Moors [ 03/Jan/14 ]

Opened SI-8114 to track this one.

Generated at Tue Jun 19 03:31:19 CEST 2018 using JIRA 7.9.1#79001-sha1:60970b42586a2ec2760ed6cfe825b26961e62b9e.