Skip to content

Commit d474461

Browse files
authored
Merge pull request #6141 from dotty-staging/fix-ide-mixin
Emit mixin-related methods as artifacts, mixin forwarders as bridges
2 parents c2551b9 + 6d0f9ca commit d474461

24 files changed

+301
-82
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,11 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
388388
}
389389
debuglog(s"Potentially conflicting names for forwarders: $conflictingNames")
390390

391-
for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flag_METHOD)) {
392-
if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor || m.isExpanded)
391+
for (m0 <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flag_METHOD)) {
392+
val m = if (m0.isBridge) m0.nextOverriddenSymbol else m0
393+
if (m == NoSymbol)
394+
log(s"$m0 is a bridge method that overrides nothing, something went wrong in a previous phase.")
395+
else if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor || m.isExpanded)
393396
debuglog(s"No forwarder for '$m' from $jclassName to '$moduleClass'")
394397
else if (conflictingNames(m.name))
395398
log(s"No forwarder for $m due to conflict with ${linkedClass.info.member(m.name)}")

compiler/src/dotty/tools/backend/jvm/BackendInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
537537
def companionSymbol: Symbol
538538
def moduleClass: Symbol
539539
def enclosingClassSym: Symbol
540-
540+
def nextOverriddenSymbol: Symbol
541541

542542

543543
// members

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
431431
val Flag_METHOD: Flags = Flags.Method.bits
432432
val ExcludedForwarderFlags: Flags = {
433433
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
434-
Flags.Bridge | Flags.Private | Flags.Macro
434+
Flags.Private | Flags.Macro
435435
}.bits
436436

437437
def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual)
@@ -765,6 +765,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
765765
}
766766
else sym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev))
767767
} //todo is handled specially for JavaDefined symbols in scalac
768+
def nextOverriddenSymbol: Symbol = toDenot(sym).nextOverriddenSymbol
768769

769770
// members
770771
def primaryConstructor: Symbol = toDenot(sym).primaryConstructor

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,15 @@ object SymDenotations {
11401140
if (!canMatchInheritedSymbols) Iterator.empty
11411141
else overriddenFromType(owner.info)
11421142

1143+
/** Equivalent to `allOverriddenSymbols.headOption.getOrElse(NoSymbol)` but more efficient. */
1144+
final def nextOverriddenSymbol(implicit ctx: Context): Symbol = {
1145+
val overridden = allOverriddenSymbols
1146+
if (overridden.hasNext)
1147+
overridden.next
1148+
else
1149+
NoSymbol
1150+
}
1151+
11431152
/** Returns all matching symbols defined in parents of the selftype. */
11441153
final def extendedOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] =
11451154
if (!canMatchInheritedSymbols) Iterator.empty

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,
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
}

0 commit comments

Comments
 (0)