Skip to content

Commit 6d0f9ca

Browse files
committed
Emit mixin forwarders as bridges
This is the same scheme I proposed in scala/scala#7843 which sidesteps all the issues regarding mixin forwarders and generic signatures, see the discussion in that PR for more information. Tests imported from scalac, some of the comments in them might not be correct for Dotty anymore.
1 parent 6ad49e9 commit 6d0f9ca

20 files changed

+284
-78
lines changed

compiler/src/dotty/tools/dotc/transform/Mixin.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
262262
for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth))
263263
yield {
264264
util.Stats.record("mixin forwarders")
265-
transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm), forwarderRhsFn(meth)))
265+
transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth)))
266266
}
267267

268268

compiler/src/dotty/tools/dotc/transform/MixinOps.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(implicit ctx: Cont
1818
map(n => ctx.getClassIfDefined("org.junit." + n)).
1919
filter(_.exists)
2020

21-
def mkForwarderSym(member: TermSymbol): TermSymbol = {
21+
def mkForwarderSym(member: TermSymbol, extraFlags: FlagSet = EmptyFlags): TermSymbol = {
2222
val res = member.copy(
2323
owner = cls,
2424
name = member.name.stripScala2LocalSuffix,
25-
flags = member.flags &~ Deferred | Synthetic | Artifact,
25+
flags = member.flags &~ Deferred | Synthetic | Artifact | extraFlags,
2626
info = cls.thisType.memberInfo(member)).enteredAfter(thisPhase).asTerm
2727
res.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot))
2828
res

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ derive-generic.scala
1313
deriving-interesting-prefixes.scala
1414
instances.scala
1515
instances-anonymous.scala
16+
mixin-forwarder-overload
17+
t8905

tests/run/mixin-bridge-methods.scala

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
case class Foo(x: Int)
2+
3+
trait A[X] {
4+
def concat[Dummy](suffix: Int): Dummy = ???
5+
}
6+
7+
class Bar extends A[Foo] {
8+
def concat(suffix: Int): Foo = Foo(0)
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class Test {
2+
public static void main(String[] args) {
3+
Bar bar = new Bar();
4+
Foo x = bar.concat(0);
5+
System.out.println(x);
6+
}
7+
}

tests/run/mixin-signatures.check

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
class Test$bar1$ {
2+
public java.lang.Object Test$bar1$.f(java.lang.Object) <bridge> <synthetic>
3+
public java.lang.String Test$bar1$.f(java.lang.Object) <bridge> <synthetic>
4+
public java.lang.String Test$bar1$.g(java.lang.String)
5+
public java.lang.Object Test$bar1$.g(java.lang.Object) <bridge> <synthetic>
6+
public java.lang.String Test$bar1$.g(java.lang.Object) <bridge> <synthetic>
7+
public java.lang.Object Test$bar1$.h(java.lang.Object) <bridge> <synthetic>
8+
}
9+
10+
class Test$bar2$ {
11+
public java.lang.Object Test$bar2$.f(java.lang.Object) <bridge> <synthetic>
12+
public java.lang.Object Test$bar2$.f(java.lang.String) <bridge> <synthetic>
13+
public java.lang.String Test$bar2$.g(java.lang.String)
14+
public java.lang.Object Test$bar2$.g(java.lang.Object) <bridge> <synthetic>
15+
public java.lang.Object Test$bar2$.g(java.lang.String) <bridge> <synthetic>
16+
public java.lang.Object Test$bar2$.h(java.lang.Object) <bridge> <synthetic>
17+
}
18+
19+
class Test$bar3$ {
20+
public java.lang.String Foo3.f(java.lang.Object)
21+
generic: public java.lang.String Foo3.f(T)
22+
public java.lang.Object Foo3.f(java.lang.Object) <bridge> <synthetic>
23+
public java.lang.String Test$bar3$.g(java.lang.String)
24+
public java.lang.Object Test$bar3$.g(java.lang.Object) <bridge> <synthetic>
25+
public java.lang.String Test$bar3$.g(java.lang.Object) <bridge> <synthetic>
26+
public java.lang.Object Foo3.h(java.lang.Object) <bridge> <synthetic>
27+
}
28+
29+
class Test$bar4$ {
30+
public java.lang.Object Foo4.f(java.lang.String)
31+
generic: public R Foo4.f(java.lang.String)
32+
public java.lang.Object Foo4.f(java.lang.Object) <bridge> <synthetic>
33+
public java.lang.String Test$bar4$.g(java.lang.String)
34+
public java.lang.Object Test$bar4$.g(java.lang.Object) <bridge> <synthetic>
35+
public java.lang.Object Test$bar4$.g(java.lang.String) <bridge> <synthetic>
36+
public java.lang.Object Foo4.h(java.lang.Object) <bridge> <synthetic>
37+
}
38+
39+
class Test$bar5$ {
40+
public java.lang.String Test$bar5$.f(java.lang.String)
41+
public java.lang.Object Test$bar5$.f(java.lang.Object) <bridge> <synthetic>
42+
public java.lang.Object Test$bar5$.f(java.lang.String) <bridge> <synthetic>
43+
public java.lang.String Test$bar5$.f(java.lang.Object) <bridge> <synthetic>
44+
public java.lang.String Test$bar5$.g(java.lang.String)
45+
public java.lang.Object Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
46+
public java.lang.Object Test$bar5$.g(java.lang.String) <bridge> <synthetic>
47+
public java.lang.String Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
48+
public java.lang.Object Test$bar5$.h(java.lang.Object) <bridge> <synthetic>
49+
}
50+
51+
interface Foo1 {
52+
public abstract java.lang.Object Base.f(java.lang.Object)
53+
generic: public abstract R Base.f(T)
54+
public default java.lang.String Foo1.f(java.lang.Object)
55+
generic: public default java.lang.String Foo1.f(T)
56+
public abstract java.lang.Object Base.g(java.lang.Object)
57+
generic: public abstract R Base.g(T)
58+
public abstract java.lang.String Foo1.g(java.lang.Object)
59+
generic: public abstract java.lang.String Foo1.g(T)
60+
public default java.lang.Object Base.h(java.lang.Object)
61+
generic: public default R Base.h(T)
62+
}
63+
64+
interface Foo2 {
65+
public abstract java.lang.Object Base.f(java.lang.Object)
66+
generic: public abstract R Base.f(T)
67+
public default java.lang.Object Foo2.f(java.lang.String)
68+
generic: public default R Foo2.f(java.lang.String)
69+
public abstract java.lang.Object Base.g(java.lang.Object)
70+
generic: public abstract R Base.g(T)
71+
public abstract java.lang.Object Foo2.g(java.lang.String)
72+
generic: public abstract R Foo2.g(java.lang.String)
73+
public default java.lang.Object Base.h(java.lang.Object)
74+
generic: public default R Base.h(T)
75+
}
76+
77+
000000000000000000000000000000000000

tests/run/mixin-signatures.scala

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
trait Base[T, R] {
2+
def f(x: T): R
3+
def g(x: T): R
4+
def h(x: T): R = null.asInstanceOf[R]
5+
}
6+
7+
trait Foo1[T] extends Base[T, String] {
8+
def f(x: T): String = null
9+
def g(x: T): String
10+
}
11+
trait Foo2[R] extends Base[String, R] {
12+
def f(x: String): R = { print(x.length) ; null.asInstanceOf[R] }
13+
def g(x: String): R
14+
}
15+
abstract class Foo3[T] extends Base[T, String] {
16+
def f(x: T): String = ""
17+
def g(x: T): String
18+
}
19+
abstract class Foo4[R] extends Base[String, R] {
20+
def f(x: String): R = { print(x.length) ; null.asInstanceOf[R] }
21+
def g(x: String): R
22+
}
23+
24+
object Test {
25+
object bar1 extends Foo1[String] { def g(x: String): String = { print(x.length) ; "" } }
26+
object bar2 extends Foo2[String] { def g(x: String): String = { print(x.length) ; "" } }
27+
object bar3 extends Foo3[String] { def g(x: String): String = { print(x.length) ; "" } }
28+
object bar4 extends Foo4[String] { def g(x: String): String = { print(x.length) ; "" } }
29+
30+
// Notice that in bar5, f and g require THREE bridges, because the final
31+
// implementation is (String)String, but:
32+
//
33+
// inherited abstract signatures: T(R), (T)String, and (String)R
34+
// which erase to: (Object)Object, (Object)String, and (String)Object
35+
//
36+
// each of which must be bridged to the actual (String)String implementation.
37+
//
38+
// public java.lang.String Test$bar5$.g(java.lang.String)
39+
// public java.lang.Object Test$bar5$.g(java.lang.String) <bridge> <synthetic>
40+
// public java.lang.Object Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
41+
// public java.lang.String Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
42+
object bar5 extends Foo1[String] with Foo2[String] {
43+
override def f(x: String): String = { print(x.length) ; x }
44+
def g(x: String): String = { print(x.length) ; x }
45+
}
46+
47+
final def m1[T, R](x: Base[T, R], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
48+
final def m2[T](x: Base[T, String], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
49+
final def m3[R](x: Base[String, R]) = { x.f("") ; x.g("") ; x.h("") }
50+
final def m4(x: Base[String, String]) = { x.f("") ; x.g("") ; x.h("") }
51+
52+
final def m11[T](x: Foo1[T], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
53+
final def m12(x: Foo1[String]) = { x.f("") ; x.g("") ; x.h("") }
54+
final def m21[T](x: Foo2[T], y: T) = { x.f("") ; x.g("") ; x.h("") }
55+
final def m22(x: Foo2[String]) = { x.f("") ; x.g("") ; x.h("") }
56+
final def m31[T](x: Foo3[T], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
57+
final def m32(x: Foo3[String]) = { x.f("") ; x.g("") ; x.h("") }
58+
final def m41[T](x: Foo4[T], y: T) = { x.f("") ; x.g("") ; x.h("") }
59+
final def m42(x: Foo4[String]) = { x.f("") ; x.g("") ; x.h("") }
60+
61+
def go = {
62+
m1(bar1, "") ; m2(bar1, "") ; m3(bar1) ; m4(bar1)
63+
m1(bar2, "") ; m2(bar2, "") ; m3(bar2) ; m4(bar2)
64+
m1(bar3, "") ; m2(bar3, "") ; m3(bar3) ; m4(bar3)
65+
m1(bar4, "") ; m2(bar4, "") ; m3(bar4) ; m4(bar4)
66+
67+
m11(bar1, "") ; m12(bar1)
68+
m21(bar2, "") ; m22(bar2)
69+
m31(bar3, "") ; m32(bar3)
70+
m41(bar4, "") ; m42(bar4)
71+
""
72+
}
73+
74+
def flagsString(m: java.lang.reflect.Method) = {
75+
val str = List(
76+
if (m.isBridge) "<bridge>" else "",
77+
if (m.isSynthetic) "<synthetic>" else ""
78+
) filterNot (_ == "") mkString " "
79+
80+
if (str == "") "" else " " + str
81+
//
82+
// val flags = scala.reflect.internal.ClassfileConstants.toScalaMethodFlags(m.getModifiers())
83+
// scala.tools.nsc.symtab.Flags.flagsToString(flags)
84+
}
85+
86+
def show(clazz: Class[_]): Unit = {
87+
print(clazz.toString + " {")
88+
clazz.getMethods.sortBy(x => (x.getName, x.isBridge, x.toString)) filter (_.getName.length == 1) foreach { m =>
89+
print("\n " + m + flagsString(m))
90+
if ("" + m != "" + m.toGenericString) {
91+
print("\n generic: " + m.toGenericString)
92+
}
93+
}
94+
println("\n}")
95+
println("")
96+
}
97+
def show(x: AnyRef): Unit = { show(x.getClass) }
98+
def show(x: String): Unit = { show(Class.forName(x)) }
99+
100+
def main(args: Array[String]): Unit = {
101+
List(bar1, bar2, bar3, bar4, bar5) foreach show
102+
List("Foo1", "Foo2") foreach show
103+
println(go)
104+
}
105+
}

tests/run/t3452b-bcode/J_2.java

Lines changed: 0 additions & 6 deletions
This file was deleted.

tests/run/t3452b-bcode/S_1.scala

Lines changed: 0 additions & 17 deletions
This file was deleted.

tests/run/t3452b-bcode/S_3.scala

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/run/t3452d/A.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ trait TraversableLike[A, Repr] {
22
def tail: Repr = null.asInstanceOf[Repr]
33
}
44

5-
abstract class AbstractTrav[A] extends TraversableLike[A, Traversable[A]]
5+
abstract class AbstractTrav[A] extends TraversableLike[A, Iterable[A]]
66

77
class C[A] extends AbstractTrav[A]

tests/run/t3452d/Test.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
import scala.collection.immutable.Nil;
2-
import scala.collection.immutable.List;
3-
import scala.collection.Traversable;
4-
51
public class Test {
62
public static void main(String[] args) {
73
C<String> c = new C<String>();
8-
// TODO add a bridge during mixin so we can expose
9-
// sharper generic signature for `tail`.
10-
/*Traversable<String>*/ Object ls = c.tail();
4+
scala.collection.Iterable<String> ls = c.tail();
115
}
126
}

tests/run/t3452g/A.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ trait TraversableLike[A, Repr] {
44

55
abstract class AbstractTrav[A] extends TraversableLike[A, AbstractTrav[A]]
66

7+
class C1 extends AbstractTrav[String]
8+
79
object O extends AbstractTrav[String]
810

9-
class C[A] extends AbstractTrav[A]
11+
class C2[A] extends AbstractTrav[A]

tests/run/t3452g/Test.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
21
public class Test {
3-
public static void main(String[] args) {
4-
// To get better types here, we would need to
5-
// add bridge during mixin so we can expose
6-
// a generic return type of Traversable<A>, because the erasure
7-
// of this (Traversable) differs from the erasure of the mixed
8-
// method (erasure(Repr) = Object)
2+
public static void main(String[] args) {
3+
AbstractTrav<String> lsSharp1 = new C1().tail();
94

10-
Object lsSharp = O.tail();
5+
// Object is the result type for the static forwarder (might be because of #11305)
6+
Object lsSharp2 = O.tail();
117

12-
Object lsSharp2 = new C<String>().tail();
13-
}
8+
AbstractTrav<String> lsSharp3 = new C2<String>().tail();
9+
}
1410
}

tests/run/t3452h.scala

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
1-
class Mix extends Foo with Bar { f; }
1+
class Mix___eFoo_I_wBar__f extends Foo_I_ with Bar__f { f; }
22
trait T
3-
abstract class Foo {
4-
class I extends T
5-
def f: I
6-
f
7-
}
8-
trait Bar {
9-
type I >: Null <: T
10-
def f: I = null
11-
f
12-
def gobble: I = null
13-
}
3+
abstract class Foo_I_ { class I extends T ; def f: I ; f; }
4+
trait Bar__f { type I>:Null<:T; def f: I = {null}; f; def gobble: I = {null}}
145

15-
object Test extends dotty.runtime.LegacyApp {
16-
new Mix
6+
object Test extends App {
7+
new Mix___eFoo_I_wBar__f
178
}

tests/run/t7932.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
public Category C.category()
2+
public Category C.category1()
3+
public default Category<F> M1.category()
4+
public default Category<scala.Tuple2> M1.category1()
5+
public default Category<F> M2.category()
6+
public default Category<scala.Tuple2> M2.category1()

tests/run/t7932.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import scala.language.higherKinds
2+
3+
class Category[M[_, _]]
4+
5+
trait M1[F] {
6+
type X[a, b] = F
7+
def category: Category[X] = null
8+
def category1: Category[Tuple2] = null
9+
}
10+
11+
// The second trait is needed to make sure there's a forwarder generated in C.
12+
// otherwise the trait methods are just the inherited default methods from M1.
13+
trait M2[F] { self: M1[F] =>
14+
override def category: Category[X] = null
15+
override def category1: Category[Tuple2] = null
16+
}
17+
18+
abstract class C extends M1[Float] with M2[Float]
19+
20+
object Test {
21+
def t(c: Class[_]) = {
22+
val ms = c.getMethods.filter(_.getName.startsWith("category"))
23+
println(ms.map(_.toGenericString).sorted.mkString("\n"))
24+
}
25+
def main(args: Array[String]): Unit = {
26+
t(classOf[C])
27+
t(classOf[M1[_]])
28+
t(classOf[M2[_]])
29+
}
30+
}

0 commit comments

Comments
 (0)