2

I'm playing around with value classes (class that extends AnyVal) in Scala 2.10.3 but are running into a strange compiler error when using them as parameter to abstract methods. As the following example demonstrates:

class ValueClass(val x: Int) extends AnyVal

trait Test {
  def foo(v: ValueClass): Int
}

new Test {
  override def foo(v: ValueClass): Int = 1
}

The compiler spits out the following error:

error: bridge generated for member method foo: (v: ValueClass)Int in anonymous class $anon
which overrides method foo: (v: ValueClass)Int in trait Test
clashes with definition of the member itself;
both have erased type (v: Int)Int
          override def foo(v: ValueClass): Int = 1

Why doesn't this work? And is there a way to pass a value class into an abstract method?

mbergenlid
  • 23
  • 2

1 Answers1

2

So as others noted, this issue has been fixed in later versions. If you are curious at all as to what was changed, I suggest you take a look into this pull request.

SI-6260 Avoid double-def error with lambdas over value classes Post-erasure of value classs in method signatures to the underlying type wreaks havoc when the erased signature overlaps with the generic signature from an overriden method. There just isn't room for both. But we really need both; callers to the interface method will be passing boxed values that the bridge needs to unbox and pass to the specific method that accepts unboxed values.

This most commonly turns up with value classes that erase to Object that are used as the parameter or the return type of an anonymous function.

This was thought to have been intractable, unless we chose a different name for the unboxed, specific method in the subclass. But that sounds like a big task that would require call-site rewriting, ala specialization.

But there is an important special case in which we don't need to rewrite call sites. If the class defining the method is anonymous, there is actually no need for the unboxed method; it will only ever be called via the generic method.

I came to this realisation when looking at how Java 8 lambdas are handled. I was expecting bridge methods, but found none. The lambda body is placed directly in a method exactly matching the generic signature.

This commit detects the clash between bridge and target, and recovers for anonymous classes by mangling the name of the target method's symbol. This is used as the bytecode name. The generic bridge forward to that, as before, with the requisite box/unbox operations.

mistahenry
  • 8,554
  • 3
  • 27
  • 38