diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index 1b2d3e79c9a4..3a23c2757760 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -67,9 +67,16 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) { def generateSerializationForwarder: Boolean = (meth.name == nme.readResolve || meth.name == nme.writeReplace) && meth.info.paramNamess.flatten.isEmpty + // Even when mixinForwarderChoices option is true, we might not want to generate certain + // mixin forwarders to avoid discrepancies between java getInterfaces() and getGenericInterfaces(), + // as adding some unnecessary mixin forwarders forces us to extend the `interfaces` array + // of the generated class in its classfile + def isJavaInterfaceIndirectlyExtended = + meth.owner.isAllOf(Flags.JavaInterface) && !cls.parentSyms.contains(meth.owner) + !meth.isConstructor && meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && - (ctx.settings.mixinForwarderChoices.isTruthy || meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition || + ((ctx.settings.mixinForwarderChoices.isTruthy && !isJavaInterfaceIndirectlyExtended)|| meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition || generateJUnitForwarder || generateSerializationForwarder) && isInImplementingClass(meth) } diff --git a/tests/run/i21177a/BaseRootJava.java b/tests/run/i21177a/BaseRootJava.java new file mode 100644 index 000000000000..efdb5131f94c --- /dev/null +++ b/tests/run/i21177a/BaseRootJava.java @@ -0,0 +1,4 @@ +public interface BaseRootJava { + default void someMethod() { + } +} diff --git a/tests/run/i21177a/GenericBaseIntermidiateJava.java b/tests/run/i21177a/GenericBaseIntermidiateJava.java new file mode 100644 index 000000000000..6b39357272a6 --- /dev/null +++ b/tests/run/i21177a/GenericBaseIntermidiateJava.java @@ -0,0 +1,2 @@ +public interface GenericBaseIntermidiateJava extends BaseRootJava { +} \ No newline at end of file diff --git a/tests/run/i21177a/Test.scala b/tests/run/i21177a/Test.scala new file mode 100644 index 000000000000..2d0fd858a9be --- /dev/null +++ b/tests/run/i21177a/Test.scala @@ -0,0 +1,12 @@ +class ChildScala extends GenericBaseIntermidiateJava[Int] { + // override def someMethod() = ??? +} +object Test { + def main(args: Array[String]): Unit = { + val c = classOf[ChildScala] + assert( + c.getGenericInterfaces.length == c.getInterfaces.length, + s"mismatch between ${c.getGenericInterfaces.mkString("Array(", ", ", ")")} and ${c.getInterfaces.mkString("Array(", ", ", ")")}" + ) + } +} \ No newline at end of file diff --git a/tests/run/i21177b/A.java b/tests/run/i21177b/A.java new file mode 100644 index 000000000000..c7ff0592cf9f --- /dev/null +++ b/tests/run/i21177b/A.java @@ -0,0 +1 @@ +interface A { default int m() { return 1; } } \ No newline at end of file diff --git a/tests/run/i21177b/Test.scala b/tests/run/i21177b/Test.scala new file mode 100644 index 000000000000..9054ac6d5f74 --- /dev/null +++ b/tests/run/i21177b/Test.scala @@ -0,0 +1,11 @@ +abstract class B { def m(): Int } +trait T extends B with A +class C extends T + +object Test { + def main(args: Array[String]): Unit = { + val cls = classOf[C] + assert(cls.getInterfaces().length == cls.getGenericInterfaces().length) + new C().m() // lack of the mixin forwarder for m() will crash this on runtime + } +}