bug icon indicating copy to clipboard operation
bug copied to clipboard

final val overriding def: Duplicate method name

Open Sciss opened this issue 4 years ago • 8 comments

reproduction steps

using Scala 2.13.4, example:

case object Foo extends Product {
  override final val productPrefix = "Foo"
}

println(Foo)

problem

Causes a runtime error in the class loader:

java.lang.ClassFormatError: Duplicate method name "productPrefix" with signature "()Ljava.lang.String;" in class file Playground$Foo$
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at sbt.internal.ManagedClassLoader.findClass(ManagedClassLoader.java:98)
	at sbt.internal.BottomClassLoader.lambda$findClass$0(BottomClassLoader.java:56)
	at sbt.internal.ClassLoadingLock.withLock(ClassLoadingLock.java:28)
	at sbt.internal.BottomClassLoader.findClass(BottomClassLoader.java:51)
	at sbt.internal.BottomClassLoader.loadClass(BottomClassLoader.java:66)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at Playground$.delayedEndpoint$Playground$1(main.scala:7)
...

Possibly related: #8663

Sciss avatar Jan 10 '21 16:01 Sciss

Bytecode diff:

diff --git Foo$.class Foo$.class
index 2928276..9c1b63a 100644
--- Foo$.class
+++ Foo$.class
@@ -1,26 +1,27 @@

 public final class Foo$ implements scala.Product,java.io.Serializable {
   public static final Foo$ MODULE$;
     descriptor: LFoo$;

-  private static final java.lang.String productPrefix;
-    descriptor: Ljava/lang/String;
-
   public static {};
     descriptor: ()V
     Code:
 ##: new           ###                  // class Foo$
 ##: dup
 ##: invokespecial ###                 // Method "<init>":()V
 ##: putstatic     ###                 // Field MODULE$:LFoo$;
 ##: getstatic     ###                 // Field MODULE$:LFoo$;
 ##: invokestatic  ###                 // InterfaceMethod scala/Product.$init$:(Lscala/Product;)V
-##: ldc           ###                 // String Foo
-##: putstatic     ###                 // Field productPrefix:Ljava/lang/String;
 ##: return

+  public java.lang.String productPrefix();
+    descriptor: ()Ljava/lang/String;
+    Code:
+##: invokestatic  ###                 // InterfaceMethod scala/Product.productPrefix$:(Lscala/Product;)Ljava/lang/String;
+##: areturn
+
   public java.lang.String productElementName(int);
     descriptor: (I)Ljava/lang/String;
     Code:
 ##: iload_1
 ##: invokestatic  ###                 // InterfaceMethod scala/Product.productElementName$:(Lscala/Product;I)Ljava/lang/String;
@@ -30,14 +31,14 @@ public final class Foo$ implements scala.Product,java.io.Serializable {
     descriptor: ()Lscala/collection/Iterator;
     Code:
 ##: invokestatic  ###                 // InterfaceMethod scala/Product.productElementNames$:(Lscala/Product;)Lscala/collection/Iterator;
 ##: areturn

-  public java.lang.String productPrefix();
+  public final java.lang.String productPrefix();
     descriptor: ()Ljava/lang/String;
     Code:
-##: getstatic     ###                 // Field productPrefix:Ljava/lang/String;
+##: ldc           ###                 // String Foo
 ##: areturn

   public int productArity();
     descriptor: ()I
     Code:

The static field and initialising it disappears and productPrefix has the string inlined, but then there's also the erroneous duplicate method.

dwijnand avatar Jan 11 '21 09:01 dwijnand

Also broken in 2.12.12 and 2.11.12, works in 2.10.7.

dwijnand avatar Jan 11 '21 09:01 dwijnand

(note that extends Product is redundant, so that can be removed from the minimization)

SethTisue avatar Jan 11 '21 22:01 SethTisue

works in Scala 3, which has me wondering if the cause is actually before the back end

SethTisue avatar Jan 11 '21 22:01 SethTisue

An attempt was made in scala/scala#9431.

dwijnand avatar Jan 20 '21 09:01 dwijnand

Workaround: add a type annotation

 case object Foo extends Product {
-  override final val productPrefix = "Foo"
+  override final val productPrefix: String = "Foo"
 }
 println(Foo)

lrytz avatar Jan 20 '21 11:01 lrytz

I just ran into this error while changing some code. If it helps identify the root cause, it seems to only happen when the subclass is a class or an object -- traits don't trigger the error.

class: https://scastie.scala-lang.org/z8frNWiOTFSM26xn8klbYQ object: https://scastie.scala-lang.org/V3JyKzSaSEWx1KH78sFWyQ trait: https://scastie.scala-lang.org/K6BUfbaaQ9mp92jA6NMeNA

mrdziuban avatar Mar 31 '22 21:03 mrdziuban

An attempt was made

A valiant attempt.

Thanks @mrdziuban I'll take a look at my lame attempt. I see lrytz left me lots of hints to follow up on.

That attempt says "Quick lunchtime fix." but it was also while company was having a security problem, very stressful period, finally they decided to move overseas. In retrospect, one wonders what was the best use of one's time.

Workaround is to use 2.10.7.

som-snytt avatar Mar 31 '22 22:03 som-snytt

Note that it happens with any method with result type that is a constant value but not AnyVal, including a Java enum. I'll take another look at the intersection of detecting matching members in mixin and whether they should survive erasure.

Also under -Xsource:3, you don't have an opportunity to have the constant type inferred!

som-snytt avatar Dec 24 '22 16:12 som-snytt