diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 248eaf73931e..9e14582b7685 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -360,7 +360,7 @@ private sealed trait XSettings: AdvancedSetting, name = "Xmixin-force-forwarders", helpArg = "mode", - descr = "Generate forwarder methods in classes inhering concrete methods from traits.", + descr = "Generate forwarder methods in classes inheriting concrete methods from traits.", choices = List("true", "junit", "false"), default = "true") diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index ce3f26071b77..b6f0ff07f764 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -95,7 +95,7 @@ object Mixin { * def x_=(y: T) = () * * 4.5 (done in `mixinForwarders`) For every method - * ` def f[Ts](ps1)...(psN): U` imn M` that needs to be disambiguated: + * ` def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: * * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) * diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index 1b2d3e79c9a4..56483500bd55 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -47,32 +47,39 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) { cls.info.nonPrivateMember(sym.name).hasAltWith(_.symbol == sym) } - /** Does `method` need a forwarder to in class `cls` - * Method needs a forwarder in those cases: + /** Does `method` need a forwarder in class `cls`? + * Method needs a forwarder in these cases: * - there's a class defining a method with same signature * - there are multiple traits defining method with same signature */ - def needsMixinForwarder(meth: Symbol): Boolean = { + def needsMixinForwarder(meth: Symbol): Boolean = lazy val competingMethods = competingMethodsIterator(meth).toList - def needsDisambiguation = competingMethods.exists(x=> !x.is(Deferred)) // multiple implementations are available + def needsDisambiguation = competingMethods.exists(!_.is(Deferred)) // multiple implementations are available def hasNonInterfaceDefinition = competingMethods.exists(!_.owner.is(Trait)) // there is a definition originating from class // JUnit 4 won't recognize annotated default methods, so always generate a forwarder for them. def generateJUnitForwarder: Boolean = - meth.annotations.nonEmpty && JUnit4Annotations.exists(annot => meth.hasAnnotation(annot)) && + meth.annotations.nonEmpty && JUnit4Annotations.exists(meth.hasAnnotation) && ctx.settings.mixinForwarderChoices.isAtLeastJunit // Similarly, Java serialization won't take into account a readResolve/writeReplace default method. def generateSerializationForwarder: Boolean = (meth.name == nme.readResolve || meth.name == nme.writeReplace) && meth.info.paramNamess.flatten.isEmpty - !meth.isConstructor && - meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && - (ctx.settings.mixinForwarderChoices.isTruthy || meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition || - generateJUnitForwarder || generateSerializationForwarder) && - isInImplementingClass(meth) - } + !meth.isConstructor + && meth.is(Method, butNot = PrivateOrAccessorOrDeferred) + && (!meth.is(JavaDefined) || !meth.owner.is(Sealed) || meth.owner.children.contains(cls)) + && ( + ctx.settings.mixinForwarderChoices.isTruthy + || meth.owner.is(Scala2x) + || needsDisambiguation + || hasNonInterfaceDefinition + || generateJUnitForwarder + || generateSerializationForwarder + ) + && isInImplementingClass(meth) + end needsMixinForwarder final val PrivateOrAccessor: FlagSet = Private | Accessor final val PrivateOrAccessorOrDeferred: FlagSet = Private | Accessor | Deferred diff --git a/tests/run/i23479/NonSeal.java b/tests/run/i23479/NonSeal.java new file mode 100644 index 000000000000..214ff5e58fc6 --- /dev/null +++ b/tests/run/i23479/NonSeal.java @@ -0,0 +1,3 @@ +// test: -jvm 17+ +public non-sealed interface NonSeal extends Seal { +} diff --git a/tests/run/i23479/Seal.java b/tests/run/i23479/Seal.java new file mode 100644 index 000000000000..53a20ddd7d25 --- /dev/null +++ b/tests/run/i23479/Seal.java @@ -0,0 +1,6 @@ + +public sealed interface Seal permits NonSeal { + default int g() { + return 42; + } +} diff --git a/tests/run/i23479/test.scala b/tests/run/i23479/test.scala new file mode 100644 index 000000000000..7380afe564d7 --- /dev/null +++ b/tests/run/i23479/test.scala @@ -0,0 +1,4 @@ +// scalajs: --skip +class C() extends NonSeal + +@main def Test = C() diff --git a/tests/run/i23479b.scala b/tests/run/i23479b.scala new file mode 100644 index 000000000000..e4f1ba3e16eb --- /dev/null +++ b/tests/run/i23479b.scala @@ -0,0 +1,13 @@ +// scalajs: --skip +trait Ord[A] + +object Ord: + sealed trait Rev[A] extends Ord[A]: + def reverse: Rev[A] = this + + trait IntOrd extends Ord[Int] + + object Int extends IntOrd with Rev[Int] + +@main def Test = + assert(Ord.Int.getClass.getDeclaredMethods.map(_.getName).toList.contains("reverse"))