Skip to content

Commit 9f505a4

Browse files
committed
Fix #1877: Add forwarders for primitive/generic mixins.
1 parent 42eb864 commit 9f505a4

8 files changed

+108
-6
lines changed

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

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx:
4848
* - there are multiple traits defining method with same signature
4949
*/
5050
def needsForwarder(meth: Symbol): Boolean = {
51-
lazy val competingMethods = cls.baseClasses.iterator
52-
.filter(_ ne meth.owner)
53-
.map(meth.overriddenSymbol)
54-
.filter(_.exists)
55-
.toList
51+
lazy val competingMethods = competingMethodsIterator(meth).toList
5652

5753
def needsDisambiguation = competingMethods.exists(x=> !(x is Deferred)) // multiple implementations are available
5854
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:
6157
(needsDisambiguation || hasNonInterfaceDefinition || meth.owner.is(Scala2x))
6258
}
6359

60+
/** Get `sym` of the method that needs a forwarder
61+
* Method needs a forwarder in those cases:
62+
* - there is a trait that defines a primitive version of implemented polymorphic method.
63+
* - there is a trait that defines a polymorphic version of implemented primitive method.
64+
*/
65+
def needsPrimitiveForwarderTo(meth: Symbol): Option[Symbol] = {
66+
def hasPrimitiveMissMatch(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match {
67+
case (tp1: MethodicType, tp2: MethodicType) =>
68+
hasPrimitiveMissMatch(tp1.resultType, tp2.resultType) ||
69+
tp1.paramTypess.flatten.zip(tp1.paramTypess.flatten).exists(args => hasPrimitiveMissMatch(args._1, args._2))
70+
case _ =>
71+
tp1.typeSymbol.isPrimitiveValueClass ^ tp2.typeSymbol.isPrimitiveValueClass
72+
}
73+
74+
def needsPrimitiveForwarder(m: Symbol): Boolean =
75+
m.owner != cls && !m.is(Deferred) && hasPrimitiveMissMatch(meth.info, m.info)
76+
77+
if (!meth.is(Method | Deferred, butNot = PrivateOrAccessor) || meth.overriddenSymbol(cls).exists || needsForwarder(meth)) None
78+
else competingMethodsIterator(meth).find(needsPrimitiveForwarder)
79+
}
80+
81+
final val PrivateOrAccessor = Private | Accessor
6482
final val PrivateOrAccessorOrDeferred = Private | Accessor | Deferred
6583

6684
def forwarder(target: Symbol) = (targs: List[Type]) => (vrefss: List[List[Tree]]) =>
6785
superRef(target).appliedToTypes(targs).appliedToArgss(vrefss)
86+
87+
private def competingMethodsIterator(meth: Symbol): Iterator[Symbol] = {
88+
cls.baseClasses.iterator
89+
.filter(_ ne meth.owner)
90+
.map(meth.overriddenSymbol)
91+
.filter(_.exists)
92+
}
6893
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ import ResolveSuper._
3838
*
3939
* <mods> def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN)
4040
*
41+
* 3.3 (done in `methodPrimitiveForwarders`) For every method that is declared both
42+
* as generic with a primitive type and with a primitive type
43+
* `<mods> def f[Ts](ps1)...(psN): U` in trait M` and
44+
* `<mods> def f[Ts](ps1)...(psN): V = ...` in implemented in N`
45+
* where U is a primitive and V a polymorphic type (or vice versa) needs:
46+
*
47+
* <mods> def f[Ts](ps1)...(psN): U = super[N].f[Ts](ps1)...(psN)
48+
*
4149
* A method in M needs to be disambiguated if it is concrete, not overridden in C,
4250
* and if it overrides another concrete method.
4351
*
@@ -65,7 +73,11 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th
6573
for (meth <- mixin.info.decls.toList if needsForwarder(meth))
6674
yield polyDefDef(implementation(meth.asTerm), forwarder(meth))
6775

68-
val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin))
76+
def methodPrimitiveForwarders: List[Tree] =
77+
for (meth <- mixins.flatMap(_.info.decls.flatMap(needsPrimitiveForwarderTo)).distinct)
78+
yield polyDefDef(implementation(meth.asTerm), forwarder(meth))
79+
80+
val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) ::: methodPrimitiveForwarders
6981

7082
cpy.Template(impl)(body = overrides ::: impl.body)
7183
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
true
2+
true
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println((new Foo: Baz).value1)
5+
println((new Foo: Baz).value2)
6+
}
7+
}
8+
9+
class Foo extends Bar[Boolean](true) with Baz
10+
11+
class Bar[T](x: T) {
12+
def value1: T = x
13+
def value2(): T = x
14+
}
15+
16+
trait Baz {
17+
def value1: Boolean
18+
def value2(): Boolean
19+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
true
2+
true
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println((new Foo: Bar[Boolean]).value1)
5+
println((new Foo: Bar[Boolean]).value2)
6+
}
7+
}
8+
9+
class Foo extends Baz with Bar[Boolean]
10+
11+
trait Bar[T] {
12+
def value1: T
13+
def value2(): T
14+
}
15+
16+
class Baz {
17+
def value1: Boolean = true
18+
def value2(): Boolean = true
19+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
true
2+
true
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println((new Foo: Baz).value)
5+
println((new Foo: Qux).value)
6+
}
7+
}
8+
9+
class Foo extends Bar[Boolean](true) with Baz with Qux
10+
11+
class Bar[T](x: T) {
12+
def value: T = x
13+
}
14+
15+
trait Baz {
16+
def value: Boolean
17+
}
18+
19+
trait Qux {
20+
def value: Boolean
21+
}

0 commit comments

Comments
 (0)