Skip to content

Commit 0af3b78

Browse files
committed
Emit Java generic signatures for mixin forwarders.
This is a forward port of scala/scala@6fc2202 In Scala 2 that commit was reverting a commit that had stopped emitting Java generic signatures. In dotc we never had that logic before, so this is new code.
1 parent b9fbf8e commit 0af3b78

File tree

8 files changed

+58
-4
lines changed

8 files changed

+58
-4
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

+10-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import dotty.tools.dotc.core.Types.*
3131
import dotty.tools.dotc.core.TypeErasure
3232
import dotty.tools.dotc.transform.GenericSignatures
3333
import dotty.tools.dotc.transform.ElimErasedValueType
34+
import dotty.tools.dotc.transform.Mixin
3435
import dotty.tools.io.AbstractFile
3536
import dotty.tools.dotc.report
3637

@@ -395,12 +396,20 @@ trait BCodeHelpers extends BCodeIdiomatic {
395396
*/
396397
def getGenericSignature(sym: Symbol, owner: Symbol): String = {
397398
atPhase(erasurePhase) {
398-
val memberTpe =
399+
def computeMemberTpe(): Type =
399400
if (sym.is(Method)) sym.denot.info
400401
else if sym.denot.validFor.phaseId > erasurePhase.id && sym.isField && sym.getter.exists then
401402
// Memoization field of getter entered after erasure, see run/i17069 for an example
402403
sym.getter.denot.info.resultType
403404
else owner.denot.thisType.memberInfo(sym)
405+
406+
val memberTpe = if sym.is(MixedIn) then
407+
mixinPhase.asInstanceOf[Mixin].mixinForwarderGenericInfos.get(sym) match
408+
case Some(genericInfo) => genericInfo
409+
case none => computeMemberTpe()
410+
else
411+
computeMemberTpe()
412+
404413
getGenericSignatureHelper(sym, owner, memberTpe).orNull
405414
}
406415
}

compiler/src/dotty/tools/dotc/core/Phases.scala

+4
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ object Phases {
239239
private var myErasurePhase: Phase = uninitialized
240240
private var myElimErasedValueTypePhase: Phase = uninitialized
241241
private var myLambdaLiftPhase: Phase = uninitialized
242+
private var myMixinPhase: Phase = uninitialized
242243
private var myCountOuterAccessesPhase: Phase = uninitialized
243244
private var myFlattenPhase: Phase = uninitialized
244245
private var myGenBCodePhase: Phase = uninitialized
@@ -266,6 +267,7 @@ object Phases {
266267
final def gettersPhase: Phase = myGettersPhase
267268
final def erasurePhase: Phase = myErasurePhase
268269
final def elimErasedValueTypePhase: Phase = myElimErasedValueTypePhase
270+
final def mixinPhase: Phase = myMixinPhase
269271
final def lambdaLiftPhase: Phase = myLambdaLiftPhase
270272
final def countOuterAccessesPhase = myCountOuterAccessesPhase
271273
final def flattenPhase: Phase = myFlattenPhase
@@ -295,6 +297,7 @@ object Phases {
295297
myErasurePhase = phaseOfClass(classOf[Erasure])
296298
myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType])
297299
myPatmatPhase = phaseOfClass(classOf[PatternMatcher])
300+
myMixinPhase = phaseOfClass(classOf[Mixin])
298301
myLambdaLiftPhase = phaseOfClass(classOf[LambdaLift])
299302
myCountOuterAccessesPhase = phaseOfClass(classOf[CountOuterAccesses])
300303
myFlattenPhase = phaseOfClass(classOf[Flatten])
@@ -551,6 +554,7 @@ object Phases {
551554
def gettersPhase(using Context): Phase = ctx.base.gettersPhase
552555
def erasurePhase(using Context): Phase = ctx.base.erasurePhase
553556
def elimErasedValueTypePhase(using Context): Phase = ctx.base.elimErasedValueTypePhase
557+
def mixinPhase(using Context): Phase = ctx.base.mixinPhase
554558
def lambdaLiftPhase(using Context): Phase = ctx.base.lambdaLiftPhase
555559
def flattenPhase(using Context): Phase = ctx.base.flattenPhase
556560
def genBCodePhase(using Context): Phase = ctx.base.genBCodePhase

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

+27-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import StdNames.*
1616
import Names.*
1717
import NameKinds.*
1818
import NameOps.*
19+
import Phases.erasurePhase
1920
import ast.Trees.*
2021

2122
import dotty.tools.dotc.transform.sjs.JSSymUtils.isJSType
@@ -115,6 +116,15 @@ object Mixin {
115116
class Mixin extends MiniPhase with SymTransformer { thisPhase =>
116117
import ast.tpd.*
117118

119+
/** Infos before erasure of the generated mixin forwarders.
120+
*
121+
* These will be used to generate Java generic signatures of the mixin
122+
* forwarders. Normally we use the types before erasure; we cannot do that
123+
* for mixin forwarders since they are created after erasure, and therefore
124+
* their type history does not have anything recorded for before erasure.
125+
*/
126+
val mixinForwarderGenericInfos = MutableSymbolMap[Type]()
127+
118128
override def phaseName: String = Mixin.name
119129

120130
override def description: String = Mixin.description
@@ -306,8 +316,24 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
306316
for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth))
307317
yield {
308318
util.Stats.record("mixin forwarders")
309-
transformFollowing(DefDef(mkForwarderSym(meth.asTerm, extraFlags = MixedIn), forwarderRhsFn(meth)))
319+
transformFollowing(DefDef(mkMixinForwarderSym(meth.asTerm), forwarderRhsFn(meth)))
320+
}
321+
322+
def mkMixinForwarderSym(target: TermSymbol): TermSymbol =
323+
val sym = mkForwarderSym(target, extraFlags = MixedIn)
324+
val infoBeforeErasure = atPhase(erasurePhase) {
325+
cls.thisType.memberInfo(target)
310326
}
327+
if !(infoBeforeErasure =:= sym.info) then
328+
// The info before erasure would not have been the same as the info now.
329+
// We want to store it for the backend to compute the generic Java signature.
330+
// However, we must still avoid doing that if erasing that signature would
331+
// not give the same erased type. If it doesn't, we'll just give a completely
332+
// incorrect Java signature. (This could be improved by generating dedicated
333+
// bridges, but we don't go that far; scalac doesn't either.)
334+
if TypeErasure.transformInfo(target, infoBeforeErasure) =:= sym.info then
335+
mixinForwarderGenericInfos(sym) = infoBeforeErasure
336+
sym
311337

312338
cpy.Template(impl)(
313339
constr =

tests/pos/11484/A_2.java

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public class A_2 extends C<String> { }

tests/pos/11484/C_1.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class B[A]
2+
sealed trait T[A] {
3+
def overloaded(that: List[T[A]]): T[A] = that.head
4+
def overloaded(that: List[B[A]]): B[A] = that.head
5+
}
6+
abstract class C[A] extends T[A]

tests/pos/11512/A_2.java

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public class A_2 extends C { }

tests/pos/11512/C_1.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trait T { this: U =>
2+
def m: Int
3+
}
4+
trait U {
5+
def m: Int = ???
6+
}
7+
abstract class C extends U with T

tests/run/t7932.check

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
public Category C.category()
2-
public Category C.category1()
1+
public Category<java.lang.Object> C.category()
2+
public Category<scala.Tuple2> C.category1()
33
public abstract Category<?> M2.category3()
44
public abstract Category<java.lang.Object> M2.category2()
55
public default Category<F> M1.category()

0 commit comments

Comments
 (0)