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

Wrong method signatures generated for class inheriting from trait specialized on AnyRef #6101

Open
scabug opened this issue Jul 18, 2012 · 6 comments

Comments

@scabug
Copy link

scabug commented Jul 18, 2012

We generate wrong method signarues for AnyRef specialization. Let me give you the following example:

package foo
import scala.runtime.BoxedUnit

trait CustomFunction1[@specialized(scala.Int) T1, @specialized(scala.AnyRef) R] extends AnyRef {
  def apply(v1: T1): R
}
class UnitFunctionBridge[T] extends CustomFunction1[T, BoxedUnit] {
  override final def apply(t: T): BoxedUnit = BoxedUnit.UNIT
}

Let's see what -Xprint:cleanup gives us:

package foo {
  abstract trait CustomFunction1 extends Object {
    def apply(v1: Object): Object;
    <specialized> def apply$mcLI$sp(v1: Int): Object
  };
  class UnitFunctionBridge extends Object with foo.CustomFunction1 {
    <specialized> def apply$mcLI$sp(v1: Int): Object = foo.CustomFunction1$class.apply$mcLI$sp(UnitFunctionBridge.this, v1);
    final override def apply(t: Object): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT;
    final override <bridge> def apply(v1: Object): Object = UnitFunctionBridge.this.apply(v1);
    def <init>(): foo.UnitFunctionBridge = {
      UnitFunctionBridge.super.<init>();
      foo.CustomFunction1$class./*CustomFunction1$class*/$init$(UnitFunctionBridge.this);
      ()
    }
  };
  abstract <specialized> trait CustomFunction1$mcLI$sp extends Object with foo.CustomFunction1 {
    <specialized> def apply(v1: Int): Object
  };
  abstract trait CustomFunction1$class extends  {
    <specialized> def apply$mcLI$sp($this: foo.CustomFunction1, v1: Int): Object = $this.apply(scala.Int.box(v1));
    def /*CustomFunction1$class*/$init$($this: foo.CustomFunction1): Unit = {
      ()
    }
  };
  abstract <specialized> trait CustomFunction1$mcLI$sp$class extends  {
    def /*CustomFunction1$mcLI$sp$class*/$init$($this: foo.CustomFunction1$mcLI$sp): Unit = {
      ()
    }
  }
}

The relevant part is (from UnitFunctionBridge:
<specialized> def apply$mcLI$sp(v1: Int): Object = foo.CustomFunction1$class.apply$mcLI$sp(UnitFunctionBridge.this, v1);

The return type here is wrong. It should be BoxedUnit because we implement generic, specialized interface of CustomFuction1[T1, R] and we substitute R with BoxedUnit.

This matters because the class shown above cannot be instantiated from Java:

public class Test {
  public void newForeach() {
    new foo.UnitFunctionBridge<String>() {};
  }
}

if you try to compile you'll get:

apply$mcLI$sp(int) in foo.UnitFunctionBridge cannot implement apply$mcLI$sp(int) in foo.CustomFunction1; attempting to use incompatible return type
found   : java.lang.Object
required: scala.runtime.BoxedUnit
    new foo.UnitFunctionBridge<String>() {};
@scabug
Copy link
Author

scabug commented Jul 18, 2012

Imported From: https://issues.scala-lang.org/browse/SI-6101?orig=1
Reporter: @gkossakowski
Affected Versions: 2.10.0
Blocks #5976

@scabug
Copy link
Author

scabug commented Jul 18, 2012

@gkossakowski said:
I forgot to mention that I put critical priority because we generate wrong byte-code and we have a potential of breaking Eclipse (as usual with wrong signatures).

@scabug
Copy link
Author

scabug commented Jul 18, 2012

@axel22 said:
Grzegorz and I were looking at this one for two hours now, and we concluded that it's an error in mixin. See:

#6103

@scabug
Copy link
Author

scabug commented Jul 18, 2012

@axel22 said (edited on Jul 18, 2012 3:33:47 PM UTC):
Explanation: we think that the bridge method for apply$mcLI$sp is something that should be added in mixin, as the specialization leaves enough information for it to do so.

The trees after specialize for the Scala file example from the bug report body:

package <empty> {
  abstract trait CustomFunction1[@specialized(scala.Int) T1 >: Nothing <: Any, @specialized(scala.`package`.AnyRef) R >: Nothing <: Any] extends Object {
    def apply(v1: T1): R;
    <specialized> def apply$mcLI$sp(v1: Int): R = CustomFunction1.this.apply(v1.asInstanceOf[T1]())
  };
  class UnitFunctionBridge[T >: Nothing <: Any] extends Object with CustomFunction1[T,scala.runtime.BoxedUnit] {
    def <init>(): UnitFunctionBridge[T] = {
      UnitFunctionBridge.super.<init>();
      ()
    };
    final override def apply(t: T): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT
  };
  abstract <specialized> trait CustomFunction1$mcLI$sp[R$sp >: Nothing <: AnyRef] extends Object with CustomFunction1[Int,R$sp] {
    <specialized> def apply(v1: Int): R$sp
  }
}

Observe apply$mcLI$sp and how its return type is R, not AnyRef. Also, note that this does not happen with abstract classes.

We think that mixin should add a bridge method in UnitFunctionBridge with a more refined return type. Instead, R is changed into Object during erasure, and mixin does not generate a version of apply$mcLI$sp with the return type BoxedUnit.

The other bug describes how generating this bridge is not done even when there is no specialization.

@scabug
Copy link
Author

scabug commented Jul 24, 2012

@VladUreche said:
Scala Meeting resolution: We'll eliminate AnyRef specialization from Function* and put AnyRef specialization under a flag, since it's not yet ripe for inclusion into trunk.

@scabug
Copy link
Author

scabug commented Aug 7, 2012

@VladUreche said:
Changed to Major, as the bug is not fixed, but is addressed by removing AnyRef specialization from the library and not making it available by default.

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

1 participant