aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/dotty/tools/dotc/transform/MixinOps.scala35
-rw-r--r--compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala14
-rw-r--r--tests/run/mixin-primitive-on-generic-1.check2
-rw-r--r--tests/run/mixin-primitive-on-generic-1.scala19
-rw-r--r--tests/run/mixin-primitive-on-generic-2.check2
-rw-r--r--tests/run/mixin-primitive-on-generic-2.scala19
-rw-r--r--tests/run/mixin-primitive-on-generic-3.check2
-rw-r--r--tests/run/mixin-primitive-on-generic-3.scala21
8 files changed, 108 insertions, 6 deletions
diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala
index 6cebf7197..c39e72b45 100644
--- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala
+++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala
@@ -48,11 +48,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx:
* - there are multiple traits defining method with same signature
*/
def needsForwarder(meth: Symbol): Boolean = {
- lazy val competingMethods = cls.baseClasses.iterator
- .filter(_ ne meth.owner)
- .map(meth.overriddenSymbol)
- .filter(_.exists)
- .toList
+ lazy val competingMethods = competingMethodsIterator(meth).toList
def needsDisambiguation = competingMethods.exists(x=> !(x is Deferred)) // multiple implementations are available
def hasNonInterfaceDefinition = competingMethods.exists(!_.owner.is(Trait)) // there is a definition originating from class
@@ -61,8 +57,37 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx:
(needsDisambiguation || hasNonInterfaceDefinition || meth.owner.is(Scala2x))
}
+ /** Get `sym` of the method that needs a forwarder
+ * Method needs a forwarder in those cases:
+ * - there is a trait that defines a primitive version of implemented polymorphic method.
+ * - there is a trait that defines a polymorphic version of implemented primitive method.
+ */
+ def needsPrimitiveForwarderTo(meth: Symbol): Option[Symbol] = {
+ def hasPrimitiveMissMatch(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match {
+ case (tp1: MethodicType, tp2: MethodicType) =>
+ hasPrimitiveMissMatch(tp1.resultType, tp2.resultType) ||
+ tp1.paramTypess.flatten.zip(tp1.paramTypess.flatten).exists(args => hasPrimitiveMissMatch(args._1, args._2))
+ case _ =>
+ tp1.typeSymbol.isPrimitiveValueClass ^ tp2.typeSymbol.isPrimitiveValueClass
+ }
+
+ def needsPrimitiveForwarder(m: Symbol): Boolean =
+ m.owner != cls && !m.is(Deferred) && hasPrimitiveMissMatch(meth.info, m.info)
+
+ if (!meth.is(Method | Deferred, butNot = PrivateOrAccessor) || meth.overriddenSymbol(cls).exists || needsForwarder(meth)) None
+ else competingMethodsIterator(meth).find(needsPrimitiveForwarder)
+ }
+
+ final val PrivateOrAccessor = Private | Accessor
final val PrivateOrAccessorOrDeferred = Private | Accessor | Deferred
def forwarder(target: Symbol) = (targs: List[Type]) => (vrefss: List[List[Tree]]) =>
superRef(target).appliedToTypes(targs).appliedToArgss(vrefss)
+
+ private def competingMethodsIterator(meth: Symbol): Iterator[Symbol] = {
+ cls.baseClasses.iterator
+ .filter(_ ne meth.owner)
+ .map(meth.overriddenSymbol)
+ .filter(_.exists)
+ }
}
diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala
index e718a7e60..c4d8f5e33 100644
--- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala
@@ -38,6 +38,14 @@ import ResolveSuper._
*
* <mods> def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN)
*
+ * 3.3 (done in `methodPrimitiveForwarders`) For every method that is declared both
+ * as generic with a primitive type and with a primitive type
+ * `<mods> def f[Ts](ps1)...(psN): U` in trait M` and
+ * `<mods> def f[Ts](ps1)...(psN): V = ...` in implemented in N`
+ * where U is a primitive and V a polymorphic type (or vice versa) needs:
+ *
+ * <mods> def f[Ts](ps1)...(psN): U = super[N].f[Ts](ps1)...(psN)
+ *
* A method in M needs to be disambiguated if it is concrete, not overridden in C,
* and if it overrides another concrete method.
*
@@ -65,7 +73,11 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th
for (meth <- mixin.info.decls.toList if needsForwarder(meth))
yield polyDefDef(implementation(meth.asTerm), forwarder(meth))
- val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin))
+ def methodPrimitiveForwarders: List[Tree] =
+ for (meth <- mixins.flatMap(_.info.decls.flatMap(needsPrimitiveForwarderTo)).distinct)
+ yield polyDefDef(implementation(meth.asTerm), forwarder(meth))
+
+ val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) ::: methodPrimitiveForwarders
cpy.Template(impl)(body = overrides ::: impl.body)
}
diff --git a/tests/run/mixin-primitive-on-generic-1.check b/tests/run/mixin-primitive-on-generic-1.check
new file mode 100644
index 000000000..bb101b641
--- /dev/null
+++ b/tests/run/mixin-primitive-on-generic-1.check
@@ -0,0 +1,2 @@
+true
+true
diff --git a/tests/run/mixin-primitive-on-generic-1.scala b/tests/run/mixin-primitive-on-generic-1.scala
new file mode 100644
index 000000000..470f543ce
--- /dev/null
+++ b/tests/run/mixin-primitive-on-generic-1.scala
@@ -0,0 +1,19 @@
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ println((new Foo: Baz).value1)
+ println((new Foo: Baz).value2)
+ }
+}
+
+class Foo extends Bar[Boolean](true) with Baz
+
+class Bar[T](x: T) {
+ def value1: T = x
+ def value2(): T = x
+}
+
+trait Baz {
+ def value1: Boolean
+ def value2(): Boolean
+}
diff --git a/tests/run/mixin-primitive-on-generic-2.check b/tests/run/mixin-primitive-on-generic-2.check
new file mode 100644
index 000000000..bb101b641
--- /dev/null
+++ b/tests/run/mixin-primitive-on-generic-2.check
@@ -0,0 +1,2 @@
+true
+true
diff --git a/tests/run/mixin-primitive-on-generic-2.scala b/tests/run/mixin-primitive-on-generic-2.scala
new file mode 100644
index 000000000..37e3f6035
--- /dev/null
+++ b/tests/run/mixin-primitive-on-generic-2.scala
@@ -0,0 +1,19 @@
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ println((new Foo: Bar[Boolean]).value1)
+ println((new Foo: Bar[Boolean]).value2)
+ }
+}
+
+class Foo extends Baz with Bar[Boolean]
+
+trait Bar[T] {
+ def value1: T
+ def value2(): T
+}
+
+class Baz {
+ def value1: Boolean = true
+ def value2(): Boolean = true
+}
diff --git a/tests/run/mixin-primitive-on-generic-3.check b/tests/run/mixin-primitive-on-generic-3.check
new file mode 100644
index 000000000..bb101b641
--- /dev/null
+++ b/tests/run/mixin-primitive-on-generic-3.check
@@ -0,0 +1,2 @@
+true
+true
diff --git a/tests/run/mixin-primitive-on-generic-3.scala b/tests/run/mixin-primitive-on-generic-3.scala
new file mode 100644
index 000000000..f6ff0b63e
--- /dev/null
+++ b/tests/run/mixin-primitive-on-generic-3.scala
@@ -0,0 +1,21 @@
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ println((new Foo: Baz).value)
+ println((new Foo: Qux).value)
+ }
+}
+
+class Foo extends Bar[Boolean](true) with Baz with Qux
+
+class Bar[T](x: T) {
+ def value: T = x
+}
+
+trait Baz {
+ def value: Boolean
+}
+
+trait Qux {
+ def value: Boolean
+}