Skip to content

Commit 00e257d

Browse files
committed
Do not generate certain interface mixin forwarders if not necessary
This affects indirectly extended java interfaces, bringing the behavior closer to how scala 2 handles it. After this commit, the methods from those interfaces will not be generated, if they are unnecessary (e.g., where there is no conflict between parents when calling that method) even if `-Xmixin-force-forwarders` is set to true. Those previously added mixin forwarders caused meant that since specific parents were referenced in those methods, they had to be added to the `interfaces` array in the generated classfile (without being added to the signature, which caused an issue with getInterfaces and getGenericInterfaces not being equal).
1 parent 74b9e85 commit 00e257d

File tree

6 files changed

+38
-1
lines changed

6 files changed

+38
-1
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,16 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) {
6767
def generateSerializationForwarder: Boolean =
6868
(meth.name == nme.readResolve || meth.name == nme.writeReplace) && meth.info.paramNamess.flatten.isEmpty
6969

70+
// Even when mixinForwarderChoices option is true, we might not want to generate certain
71+
// mixin forwarders to avoid discrepancies between java getInterfaces() and getGenericInterfaces(),
72+
// as adding some unnecessary mixin forwarders forces us to extend the `interfaces` array
73+
// of the generated class in its classfile
74+
def isJavaInterfaceIndirectlyExtended =
75+
meth.owner.isAllOf(Flags.JavaInterface) && !cls.parentSyms.contains(meth.owner)
76+
7077
!meth.isConstructor &&
7178
meth.is(Method, butNot = PrivateOrAccessorOrDeferred) &&
72-
(ctx.settings.mixinForwarderChoices.isTruthy || meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition ||
79+
((ctx.settings.mixinForwarderChoices.isTruthy && !isJavaInterfaceIndirectlyExtended)|| meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition ||
7380
generateJUnitForwarder || generateSerializationForwarder) &&
7481
isInImplementingClass(meth)
7582
}

tests/run/i21177a/BaseRootJava.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public interface BaseRootJava {
2+
default void someMethod() {
3+
}
4+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
public interface GenericBaseIntermidiateJava<T> extends BaseRootJava {
2+
}

tests/run/i21177a/Test.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class ChildScala extends GenericBaseIntermidiateJava[Int] {
2+
// override def someMethod() = ???
3+
}
4+
object Test {
5+
def main(args: Array[String]): Unit = {
6+
val c = classOf[ChildScala]
7+
assert(
8+
c.getGenericInterfaces.length == c.getInterfaces.length,
9+
s"mismatch between ${c.getGenericInterfaces.mkString("Array(", ", ", ")")} and ${c.getInterfaces.mkString("Array(", ", ", ")")}"
10+
)
11+
}
12+
}

tests/run/i21177b/A.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface A { default int m() { return 1; } }

tests/run/i21177b/Test.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
abstract class B { def m(): Int }
2+
trait T extends B with A
3+
class C extends T
4+
5+
object Test {
6+
def main(args: Array[String]): Unit = {
7+
val cls = classOf[C]
8+
assert(cls.getInterfaces().length == cls.getGenericInterfaces().length)
9+
new C().m() // lack of the mixin forwarder for m() will crash this on runtime
10+
}
11+
}

0 commit comments

Comments
 (0)