Skip to content

Emit mixin-related methods as artifacts, mixin forwarders as bridges #6141

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

Merged
merged 3 commits into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,11 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}
debuglog(s"Potentially conflicting names for forwarders: $conflictingNames")

for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flag_METHOD)) {
if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor || m.isExpanded)
for (m0 <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flag_METHOD)) {
val m = if (m0.isBridge) m0.nextOverriddenSymbol else m0
if (m == NoSymbol)
log(s"$m0 is a bridge method that overrides nothing, something went wrong in a previous phase.")
else if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor || m.isExpanded)
debuglog(s"No forwarder for '$m' from $jclassName to '$moduleClass'")
else if (conflictingNames(m.name))
log(s"No forwarder for $m due to conflict with ${linkedClass.info.member(m.name)}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
def companionSymbol: Symbol
def moduleClass: Symbol
def enclosingClassSym: Symbol

def nextOverriddenSymbol: Symbol


// members
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
val Flag_METHOD: Flags = Flags.Method.bits
val ExcludedForwarderFlags: Flags = {
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
Flags.Bridge | Flags.Private | Flags.Macro
Flags.Private | Flags.Macro
}.bits

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

// members
def primaryConstructor: Symbol = toDenot(sym).primaryConstructor
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,15 @@ object SymDenotations {
if (!canMatchInheritedSymbols) Iterator.empty
else overriddenFromType(owner.info)

/** Equivalent to `allOverriddenSymbols.headOption.getOrElse(NoSymbol)` but more efficient. */
final def nextOverriddenSymbol(implicit ctx: Context): Symbol = {
val overridden = allOverriddenSymbols
if (overridden.hasNext)
overridden.next
else
NoSymbol
}

/** Returns all matching symbols defined in parents of the selftype. */
final def extendedOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] =
if (!canMatchInheritedSymbols) Iterator.empty
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth))
yield {
util.Stats.record("mixin forwarders")
transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm), forwarderRhsFn(meth)))
transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth)))
}


Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/MixinOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(implicit ctx: Cont
map(n => ctx.getClassIfDefined("org.junit." + n)).
filter(_.exists)

def mkForwarderSym(member: TermSymbol): TermSymbol = {
def mkForwarderSym(member: TermSymbol, extraFlags: FlagSet = EmptyFlags): TermSymbol = {
val res = member.copy(
owner = cls,
name = member.name.stripScala2LocalSuffix,
flags = member.flags &~ Deferred,
flags = member.flags &~ Deferred | Synthetic | Artifact | extraFlags,
info = cls.thisType.memberInfo(member)).enteredAfter(thisPhase).asTerm
res.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot))
res
Expand Down
2 changes: 2 additions & 0 deletions compiler/test/dotc/run-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ derive-generic.scala
deriving-interesting-prefixes.scala
instances.scala
instances-anonymous.scala
mixin-forwarder-overload
t8905
14 changes: 0 additions & 14 deletions tests/run/mixin-bridge-methods.scala

This file was deleted.

9 changes: 9 additions & 0 deletions tests/run/mixin-forwarder-overload/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
case class Foo(x: Int)

trait A[X] {
def concat[Dummy](suffix: Int): Dummy = ???
}

class Bar extends A[Foo] {
def concat(suffix: Int): Foo = Foo(0)
}
7 changes: 7 additions & 0 deletions tests/run/mixin-forwarder-overload/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class Test {
public static void main(String[] args) {
Bar bar = new Bar();
Foo x = bar.concat(0);
System.out.println(x);
}
}
77 changes: 77 additions & 0 deletions tests/run/mixin-signatures.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
class Test$bar1$ {
public java.lang.Object Test$bar1$.f(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar1$.f(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar1$.g(java.lang.String)
public java.lang.Object Test$bar1$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar1$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar1$.h(java.lang.Object) <bridge> <synthetic>
}

class Test$bar2$ {
public java.lang.Object Test$bar2$.f(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar2$.f(java.lang.String) <bridge> <synthetic>
public java.lang.String Test$bar2$.g(java.lang.String)
public java.lang.Object Test$bar2$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar2$.g(java.lang.String) <bridge> <synthetic>
public java.lang.Object Test$bar2$.h(java.lang.Object) <bridge> <synthetic>
}

class Test$bar3$ {
public java.lang.String Foo3.f(java.lang.Object)
generic: public java.lang.String Foo3.f(T)
public java.lang.Object Foo3.f(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar3$.g(java.lang.String)
public java.lang.Object Test$bar3$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar3$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Foo3.h(java.lang.Object) <bridge> <synthetic>
}

class Test$bar4$ {
public java.lang.Object Foo4.f(java.lang.String)
generic: public R Foo4.f(java.lang.String)
public java.lang.Object Foo4.f(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar4$.g(java.lang.String)
public java.lang.Object Test$bar4$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar4$.g(java.lang.String) <bridge> <synthetic>
public java.lang.Object Foo4.h(java.lang.Object) <bridge> <synthetic>
}

class Test$bar5$ {
public java.lang.String Test$bar5$.f(java.lang.String)
public java.lang.Object Test$bar5$.f(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar5$.f(java.lang.String) <bridge> <synthetic>
public java.lang.String Test$bar5$.f(java.lang.Object) <bridge> <synthetic>
public java.lang.String Test$bar5$.g(java.lang.String)
public java.lang.Object Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar5$.g(java.lang.String) <bridge> <synthetic>
public java.lang.String Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
public java.lang.Object Test$bar5$.h(java.lang.Object) <bridge> <synthetic>
}

interface Foo1 {
public abstract java.lang.Object Base.f(java.lang.Object)
generic: public abstract R Base.f(T)
public default java.lang.String Foo1.f(java.lang.Object)
generic: public default java.lang.String Foo1.f(T)
public abstract java.lang.Object Base.g(java.lang.Object)
generic: public abstract R Base.g(T)
public abstract java.lang.String Foo1.g(java.lang.Object)
generic: public abstract java.lang.String Foo1.g(T)
public default java.lang.Object Base.h(java.lang.Object)
generic: public default R Base.h(T)
}

interface Foo2 {
public abstract java.lang.Object Base.f(java.lang.Object)
generic: public abstract R Base.f(T)
public default java.lang.Object Foo2.f(java.lang.String)
generic: public default R Foo2.f(java.lang.String)
public abstract java.lang.Object Base.g(java.lang.Object)
generic: public abstract R Base.g(T)
public abstract java.lang.Object Foo2.g(java.lang.String)
generic: public abstract R Foo2.g(java.lang.String)
public default java.lang.Object Base.h(java.lang.Object)
generic: public default R Base.h(T)
}

000000000000000000000000000000000000
105 changes: 105 additions & 0 deletions tests/run/mixin-signatures.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
trait Base[T, R] {
def f(x: T): R
def g(x: T): R
def h(x: T): R = null.asInstanceOf[R]
}

trait Foo1[T] extends Base[T, String] {
def f(x: T): String = null
def g(x: T): String
}
trait Foo2[R] extends Base[String, R] {
def f(x: String): R = { print(x.length) ; null.asInstanceOf[R] }
def g(x: String): R
}
abstract class Foo3[T] extends Base[T, String] {
def f(x: T): String = ""
def g(x: T): String
}
abstract class Foo4[R] extends Base[String, R] {
def f(x: String): R = { print(x.length) ; null.asInstanceOf[R] }
def g(x: String): R
}

object Test {
object bar1 extends Foo1[String] { def g(x: String): String = { print(x.length) ; "" } }
object bar2 extends Foo2[String] { def g(x: String): String = { print(x.length) ; "" } }
object bar3 extends Foo3[String] { def g(x: String): String = { print(x.length) ; "" } }
object bar4 extends Foo4[String] { def g(x: String): String = { print(x.length) ; "" } }

// Notice that in bar5, f and g require THREE bridges, because the final
// implementation is (String)String, but:
//
// inherited abstract signatures: T(R), (T)String, and (String)R
// which erase to: (Object)Object, (Object)String, and (String)Object
//
// each of which must be bridged to the actual (String)String implementation.
//
// public java.lang.String Test$bar5$.g(java.lang.String)
// public java.lang.Object Test$bar5$.g(java.lang.String) <bridge> <synthetic>
// public java.lang.Object Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
// public java.lang.String Test$bar5$.g(java.lang.Object) <bridge> <synthetic>
object bar5 extends Foo1[String] with Foo2[String] {
override def f(x: String): String = { print(x.length) ; x }
def g(x: String): String = { print(x.length) ; x }
}

final def m1[T, R](x: Base[T, R], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
final def m2[T](x: Base[T, String], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
final def m3[R](x: Base[String, R]) = { x.f("") ; x.g("") ; x.h("") }
final def m4(x: Base[String, String]) = { x.f("") ; x.g("") ; x.h("") }

final def m11[T](x: Foo1[T], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
final def m12(x: Foo1[String]) = { x.f("") ; x.g("") ; x.h("") }
final def m21[T](x: Foo2[T], y: T) = { x.f("") ; x.g("") ; x.h("") }
final def m22(x: Foo2[String]) = { x.f("") ; x.g("") ; x.h("") }
final def m31[T](x: Foo3[T], y: T) = { x.f(y) ; x.g(y) ; x.h(y) }
final def m32(x: Foo3[String]) = { x.f("") ; x.g("") ; x.h("") }
final def m41[T](x: Foo4[T], y: T) = { x.f("") ; x.g("") ; x.h("") }
final def m42(x: Foo4[String]) = { x.f("") ; x.g("") ; x.h("") }

def go = {
m1(bar1, "") ; m2(bar1, "") ; m3(bar1) ; m4(bar1)
m1(bar2, "") ; m2(bar2, "") ; m3(bar2) ; m4(bar2)
m1(bar3, "") ; m2(bar3, "") ; m3(bar3) ; m4(bar3)
m1(bar4, "") ; m2(bar4, "") ; m3(bar4) ; m4(bar4)

m11(bar1, "") ; m12(bar1)
m21(bar2, "") ; m22(bar2)
m31(bar3, "") ; m32(bar3)
m41(bar4, "") ; m42(bar4)
""
}

def flagsString(m: java.lang.reflect.Method) = {
val str = List(
if (m.isBridge) "<bridge>" else "",
if (m.isSynthetic) "<synthetic>" else ""
) filterNot (_ == "") mkString " "

if (str == "") "" else " " + str
//
// val flags = scala.reflect.internal.ClassfileConstants.toScalaMethodFlags(m.getModifiers())
// scala.tools.nsc.symtab.Flags.flagsToString(flags)
}

def show(clazz: Class[_]): Unit = {
print(clazz.toString + " {")
clazz.getMethods.sortBy(x => (x.getName, x.isBridge, x.toString)) filter (_.getName.length == 1) foreach { m =>
print("\n " + m + flagsString(m))
if ("" + m != "" + m.toGenericString) {
print("\n generic: " + m.toGenericString)
}
}
println("\n}")
println("")
}
def show(x: AnyRef): Unit = { show(x.getClass) }
def show(x: String): Unit = { show(Class.forName(x)) }

def main(args: Array[String]): Unit = {
List(bar1, bar2, bar3, bar4, bar5) foreach show
List("Foo1", "Foo2") foreach show
println(go)
}
}
6 changes: 0 additions & 6 deletions tests/run/t3452b-bcode/J_2.java

This file was deleted.

17 changes: 0 additions & 17 deletions tests/run/t3452b-bcode/S_1.scala

This file was deleted.

5 changes: 0 additions & 5 deletions tests/run/t3452b-bcode/S_3.scala

This file was deleted.

2 changes: 1 addition & 1 deletion tests/run/t3452d/A.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ trait TraversableLike[A, Repr] {
def tail: Repr = null.asInstanceOf[Repr]
}

abstract class AbstractTrav[A] extends TraversableLike[A, Traversable[A]]
abstract class AbstractTrav[A] extends TraversableLike[A, Iterable[A]]

class C[A] extends AbstractTrav[A]
8 changes: 1 addition & 7 deletions tests/run/t3452d/Test.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import scala.collection.immutable.Nil;
import scala.collection.immutable.List;
import scala.collection.Traversable;

public class Test {
public static void main(String[] args) {
C<String> c = new C<String>();
// TODO add a bridge during mixin so we can expose
// sharper generic signature for `tail`.
/*Traversable<String>*/ Object ls = c.tail();
scala.collection.Iterable<String> ls = c.tail();
}
}
4 changes: 3 additions & 1 deletion tests/run/t3452g/A.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ trait TraversableLike[A, Repr] {

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

class C1 extends AbstractTrav[String]

object O extends AbstractTrav[String]

class C[A] extends AbstractTrav[A]
class C2[A] extends AbstractTrav[A]
16 changes: 6 additions & 10 deletions tests/run/t3452g/Test.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@

public class Test {
public static void main(String[] args) {
// To get better types here, we would need to
// add bridge during mixin so we can expose
// a generic return type of Traversable<A>, because the erasure
// of this (Traversable) differs from the erasure of the mixed
// method (erasure(Repr) = Object)
public static void main(String[] args) {
AbstractTrav<String> lsSharp1 = new C1().tail();

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

Object lsSharp2 = new C<String>().tail();
}
AbstractTrav<String> lsSharp3 = new C2<String>().tail();
}
}
Loading