Skip to content

Bridge or omit troublesome additional abstract methods in specialized traits #36

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

Closed
retronym opened this issue Aug 5, 2015 · 7 comments

Comments

@retronym
Copy link
Member

retronym commented Aug 5, 2015

Interaction with specialization

Specialization adds another small challenge. In the example below, notice how apply : (Int)V has been added to the specialized sub interface. A Java subclass of T$mcI$sp would need to implement both apply-s. T$mcI$sp is not a functional interface, so can't be used in LambdaMetafactory / (e.g. as the target type of a Java lambda expressions).

We run straight into this problem with the specialized Scala function traits.

% cat sandbox/spec.scala
trait T[@specialized A] {
  def t(a: A): Unit
}

% scalac sandbox/spec.scala && javap -classpath . T 'T$mcI$sp'
Compiled from "spec.scala"
public interface T{
    public abstract void t(java.lang.Object);
    public abstract void t$mcZ$sp(boolean);
    public abstract void t$mcB$sp(byte);
    public abstract void t$mcC$sp(char);
    public abstract void t$mcD$sp(double);
    public abstract void t$mcF$sp(float);
    public abstract void t$mcI$sp(int);
    public abstract void t$mcJ$sp(long);
    public abstract void t$mcS$sp(short);
    public abstract void t$mcV$sp(scala.runtime.BoxedUnit);
}

Compiled from "spec.scala"
public interface T$mcI$sp extends T{
    public abstract void t(int);
}

Contrast with a regular trait that inherits another trait:

cat sandbox/spec.scala && scalac sandbox/spec.scala && javap -classpath . U V
trait U[A] {
  def t(a: A): Unit
}
trait V extends U[Int]
Compiled from "spec.scala"
public interface U{
    public abstract void t(java.lang.Object);
}

Compiled from "spec.scala"
public interface V extends U{
}

We should investigate whether addition of this method by specialization is necessary / intentional. If we cannot omit it, we do have the possibility to bridge it.

public interface T$mcI$sp extends T{
    public abstract void t(int) = UNBOX(t$mcI$sp(BOX(arg)))
}

Scala callers never route into this method:

% cat sandbox/spec.scala && scalac sandbox/spec.scala && javap -c -classpath . Client
trait T[@specialized -A] {
  def t(a: A): Unit
}

class Client {
  def f(tint:  T[Int])= tint.t(0)
  def g[A](t: T[A], a: A) = t.t(a)
}
Compiled from "spec.scala"
public class Client extends java.lang.Object{
public void f(T);
  Code:
   0:   aload_1
   1:   iconst_0
   2:   invokeinterface #16,  2; //InterfaceMethod T.t$mcI$sp:(I)V
   7:   return

public void g(T, java.lang.Object);
  Code:
   0:   aload_1
   1:   aload_2
   2:   invokeinterface #26,  2; //InterfaceMethod T.t:(Ljava/lang/Object;)V
   7:   return

public Client();
  Code:
   0:   aload_0
   1:   invokespecial   #32; //Method java/lang/Object."<init>":()V
   4:   return

}
@VladUreche
Copy link

@retronym: The additional method can most likely be eliminated, but you'll need additional logic in specialOverrides.

@retronym
Copy link
Member Author

Thanks, @VladUreche.

I'm prototypying a change here; scala/scala@scala:2.12.x...retronym:topic/trait-default-specialization

During my digging, this old commit seemed relevant; aeda72b

@retronym
Copy link
Member Author

@VladUreche
Copy link

I'm prototypying a change here; scala/scala@scala:2.12.x...retronym:topic/trait-default-specialization

The problem is I can't grasp all the implications of this change -- the model where the generic class/trait is extended by the specializations is very difficult to reason about, esp. when it interacts with mixin 👎

Lacking a nice mental model of this, have you tried the community build with the change? 😆

@retronym
Copy link
Member Author

Here's the diff of the terminal phase trees for the the test case I was working with: https://gist.github.com/25a12fb3e6258ff0d5ee

Notice that the class TInt still has the "specialized overload":

def t(a: Int): Unit = Test$TInt$1.this.t$mcI$sp(a);

I'll also tossed this to the community build: https://scala-ci.typesafe.com/view/scala-2.12.x/job/scala-2.12.x-integrate-community-build/109/console

@VladUreche
Copy link

LGTM, the bodies you eliminate have to exist at least in the class/trait that offers an implementation for the method (and possibly in overriders). Or, at least, I can't come up with a counterexample...

retronym added a commit to retronym/scala that referenced this issue Jan 22, 2019
Introduced in 2bde392

The previous commit defends the intended part of the change,
which was discussed in scala/scala-dev#36
@dwijnand
Copy link
Member

Closing because it doesn't seem to have become an issue. But (@retronym) we can reopen and move it to scala/bug if need be.

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

3 participants