scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

Scalac generating invalid bytecode, duplicate methods #3004

Closed scabug closed 13 years ago

scabug commented 14 years ago

Ignoring the terribleness of the design of this class hierarchy, I am seeing invalid byte-code problems with 2.8.Beta1 that I didn't see in 2.7.7 land:

here is the file that causes the issue:

package bizz

trait DecXTMaker[DTM <: DecXT[DTM]] {
def mkA(ctorData: Long): DTM;
val hashPrecision: Int
}
trait DecXTFactory[DTF <: DecXT[DTF]] extends DecXTMaker[DTF] 
abstract class DecXT[DT <: DecXT[DT]](val raw: Long) extends DecXTFactory[DT]

trait DecXMaker extends DecXTMaker[DecX] {
  override def mkA(ctorData: Long): DecX = new DecX(ctorData)
  override val hashPrecision = 9
}
object DecX extends DecXTFactory[DecX] with DecXMaker
class DecX(raw: Long) extends DecXT[DecX](raw) with DecXMaker

Here is the javap output for the DecX class scalac generates:

$$ javap bizz.DecXCompiled from "Foo.scala"
public class bizz.DecX extends bizz.DecXT implements bizz.DecXMaker,scala.ScalaObject{
    public static final void bizz$$DecXMaker$$_setter_$$hashPrecision_$$eq(int);
    public bizz.DecX(long);
    public bizz.DecXT mkA(long);
    public bizz.DecX mkA(long);
    public void bizz$$DecXMaker$$_setter_$$hashPrecision_$$eq(int);
    public int hashPrecision();
}

Finally here the exception i get in java land

Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import bizz.DecX
import bizz.DecX

scala> val hp = DecX.hashPrecision
java.lang.ClassFormatError: Duplicate method name&signature in class file bizz/DecX
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
...

scala> val hp = DecX.mkA(4)       
java.lang.ClassFormatError: Duplicate method name&signature in class file bizz/DecX
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:621)

Curiously enough it if placed in the root package (aka no package name given) the bytecode generated looks like this instead!

javap DecX
Compiled from "Foo.scala"
public class DecX extends DecXT implements DecXMaker,scala.ScalaObject{
    public DecX(long);
    public DecXT mkA(long);
    public DecX mkA(long);
    public void DecXMaker$$_setter_$$hashPrecision_$$eq(int);
    public int hashPrecision();
}

and the console works

Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val hp = DecX.hashPrecision
hp: Int = 9

scala> val hp = DecX.mkA(4)       
hp: DecX = DecX@4df2868

finally just make life a little easier here is the old 2.7.7 javap's first, with no package name

Compiled from "Foo.scala"
public class DecX extends DecXT implements DecXMaker,scala.ScalaObject{
    public DecX(long);
    public DecXT mkA(long);
    public DecX mkA(long);
    public void hashPrecision_$$eq(int);
    public int hashPrecision();
}

secondly, in the bizz package (looks the same as 2.7.7 no package to me)

Compiled from "Foo.scala"
public class bizz.DecX extends bizz.DecXT implements bizz.DecXMaker,scala.ScalaObject{
    public bizz.DecX(long);
    public bizz.DecXT mkA(long);
    public bizz.DecX mkA(long);
    public void hashPrecision_$$eq(int);
    public int hashPrecision();
}

out of curiosity why is a setter method being generated for a val? ( i don't know all that much about how the stuff ends up compiling) & where is that static one coming from?

scabug commented 14 years ago

Imported From: https://issues.scala-lang.org/browse/SI-3004?orig=1 Reporter: benjackman

scabug commented 14 years ago

benjackman said: BTW as a work-around I turned the val hashPrecision into a def hashPrecision in both the trait and the concrete class and the byte-code verifies.

scabug commented 14 years ago

benjackman said: Also here is the -Xprint for the phase before (flatten) and the phase when (mixin) the setter is added: flatten:

[[syntax trees at end of flatten]]// Scala source: Foo.scala
package bizz {
  abstract trait DecXTMaker extends java.lang.Object {
    def mkA(ctorData: Long): bizz.DecXT;
    <stable> <accessor> def hashPrecision(): Int
  };
  abstract trait DecXTFactory extends java.lang.Object with bizz.DecXTMaker;
  abstract class DecXT extends java.lang.Object with bizz.DecXTFactory with ScalaObject {
    <paramaccessor> private[this] val raw: Long = _;
    <stable> <accessor> <paramaccessor> def raw(): Long = DecXT.this.raw;
    def this(raw: Long): bizz.DecXT = {
      DecXT.this.raw = raw;
      DecXT.super.this();
      ()
    }
  };
  abstract trait DecXMaker extends java.lang.Object with bizz.DecXTMaker with ScalaObject {
    override def mkA(ctorData: Long): bizz.DecX;
    override <stable> <accessor> def hashPrecision(): Int
  };
  final class DecX extends java.lang.Object with bizz.DecXTFactory with bizz.DecXMaker with ScalaObject {
    override <bridge> def mkA(ctorData: Long): bizz.DecXT = DecX.this.mkA(ctorData);
    def this(): object bizz.DecX = {
      DecX.super.this();
      DecX.this.$$asInstanceOf[bizz.DecXMaker$$class]()./*DecXMaker$$class*/$$init$$();
      ()
    }
  };
  class DecX extends bizz.DecXT with bizz.DecXMaker with ScalaObject {
    override <bridge> def mkA(ctorData: Long): bizz.DecXT = DecX.this.mkA(ctorData);
    def this(raw: Long): bizz.DecX = {
      DecX.super.this(raw);
      DecX.this.$$asInstanceOf[bizz.DecXMaker$$class]()./*DecXMaker$$class*/$$init$$();
      ()
    }
  };
  abstract trait DecXMaker$$class extends java.lang.Object with bizz.DecXTMaker with ScalaObject with bizz.DecXMaker {
    override def mkA(ctorData: Long): bizz.DecX = new bizz.DecX(ctorData);
    private[this] val hashPrecision: Int = _;
    override <stable> <accessor> def hashPrecision(): Int = DecXMaker$$class.this.hashPrecision;
    def /*DecXMaker$$class*/$$init$$(): Unit = {
      DecXMaker$$class.this.hashPrecision = 9;
      ()
    }
  }
}

mixin:

[[syntax trees at end of mixin]]// Scala source: Foo.scala
package bizz {
  abstract trait DecXTMaker extends java.lang.Object {
    def mkA(ctorData: Long): bizz.DecXT;
    <stable> <accessor> def hashPrecision(): Int
  };
  abstract trait DecXTFactory extends java.lang.Object with bizz.DecXTMaker;
  abstract class DecXT extends java.lang.Object with bizz.DecXTFactory with ScalaObject {
    <paramaccessor> private[this] val raw: Long = _;
    <stable> <accessor> <paramaccessor> def raw(): Long = DecXT.this.raw;
    def this(raw: Long): bizz.DecXT = {
      DecXT.this.raw = raw;
      DecXT.super.this();
      ()
    }
  };
  abstract trait DecXMaker extends java.lang.Object with bizz.DecXTMaker with ScalaObject {
    <accessor> def bizz$$DecXMaker$$_setter_#Precision_=(x$$1: Int): Unit;
    override def mkA(ctorData: Long): bizz.DecX;
    override <stable> <accessor> def hashPrecision(): Int
  };
  final class DecX extends java.lang.Object with bizz.DecXTFactory with bizz.DecXMaker with ScalaObject {
    override <stable> <accessor> def hashPrecision(): Int = DecX.this.hashPrecision;
    private[this] val hashPrecision: Int = _;
    <accessor> def bizz$$DecXMaker$$_setter_#Precision_=(x$$1: Int): Unit = DecX.this.hashPrecision = x$$1;
    override def mkA(ctorData: Long): bizz.DecX = bizz.DecXMaker$$class.mkA(DecX.this, ctorData);
    override <bridge> def mkA(ctorData: Long): bizz.DecXT = DecX.this.mkA(ctorData);
    def this(): object bizz.DecX = {
      DecX.super.this();
      bizz.DecXMaker$$class./*DecXMaker$$class*/$$init$$(DecX.this);
      ()
    }
  };
  class DecX extends bizz.DecXT with bizz.DecXMaker with ScalaObject {
    override <stable> <accessor> def hashPrecision(): Int = DecX.this.hashPrecision;
    private[this] val hashPrecision: Int = _;
    <accessor> def bizz$$DecXMaker$$_setter_#Precision_=(x$$1: Int): Unit = DecX.this.hashPrecision = x$$1;
    override def mkA(ctorData: Long): bizz.DecX = bizz.DecXMaker$$class.mkA(DecX.this, ctorData);
    override <bridge> def mkA(ctorData: Long): bizz.DecXT = DecX.this.mkA(ctorData);
    def this(raw: Long): bizz.DecX = {
      DecX.super.this(raw);
      bizz.DecXMaker$$class./*DecXMaker$$class*/$$init$$(DecX.this);
      ()
    }
  };
  abstract trait DecXMaker$$class extends  {
    override def mkA($$this: bizz.DecXMaker, ctorData: Long): bizz.DecX = new bizz.DecX(ctorData);
    def /*DecXMaker$$class*/$$init$$($$this: bizz.DecXMaker): Unit = {
      $$this.bizz$$DecXMaker$$_setter_#Precision_=(9);
      ()
    }
  }
}
scabug commented 14 years ago

@paulp said: You have revealed great inadequacy in the fix for #1804. I will check in a better fix shortly. (It will still be inadequate, but less so.)

scabug commented 14 years ago

@paulp said: This is fixed, for certain definitions thereof, in r20738.

To answer one of your questions, the static method is a forwarder: for any given object Foo$$ static forwarders are added to companion class Foo except when there are conflicts. The conflict logic is rather hairy. When a companion pair each inherit from the same parent, obviously a forwarder should not be generated for any methods inherited from that parent, but the name manglings and renamings (and the fact that I've had to figure everything out as I go) make correctness a slippery weasel.

scabug commented 14 years ago

benjackman said: Nice work Paul, thanks for the quick fix and explanation. I noticed that in r20738 in the comment you mentioned ticket #3003 I am not sure if that was a typo or not. Cheers.

scabug commented 14 years ago

andrei oprisan (aoprisan) said: It seems this problem is still present in 2.8.0-rc1 . The code that triggers it: {code} object MyClass { val duplicate: Int = 10 }

class MyClass { private val duplicate = MyClass.duplicate } {code}[[BR]]

Whenever an instance of MyClass is created, the following exception is thrown: {code} Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in class file duplicatemerthodsignaturescala/MyClass {code}[[BR]]

Javap output: {code} public class duplicatemerthodsignaturescala.MyClass extends java.lang.Object implements scala.ScalaObject{ private final int duplicate; public static final int duplicate(); private int duplicate(); public duplicatemerthodsignaturescala.MyClass(); } {code}[[BR]]

JVM version: 1.6.0_18; Scala version: 2.8.0.RC1; OS: Ubuntu 9.10.

scabug commented 14 years ago

andrei oprisan (aoprisan) said: I apologize for reposting the same thing, but the first time I did a mistake in the formatting :( and I couldn't edit the original comment.

It seems this problem is still present in 2.8.0-rc1 . The code that triggers it:

object MyClass {
  val duplicate: Int = 10
}

class MyClass {
  private val duplicate = MyClass.duplicate
}

Whenever an instance of !MyClass is created, the following exception is thrown:

Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in class file duplicatemerthodsignaturescala/MyClass

Javap output:


public class duplicatemerthodsignaturescala.MyClass extends java.lang.Object implements scala.ScalaObject{
    private final int duplicate;
    public static final int duplicate();
    private int duplicate();
    public duplicatemerthodsignaturescala.MyClass();
}

JVM version: 1.6.0_18; Scala version: 2.8.0.RC1; OS: Ubuntu 9.10.

scabug commented 14 years ago

@paulp said: (In r21611) Fix and test case for forwarder duplicate bug. Closes #3004, no review.