Skip to content

Commit

Permalink
Emit Java generic signatures for mixin forwarders.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sjrd committed Nov 12, 2024
1 parent 3b928c5 commit a90225b
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 4 deletions.
11 changes: 10 additions & 1 deletion compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.core.TypeErasure
import dotty.tools.dotc.transform.GenericSignatures
import dotty.tools.dotc.transform.ElimErasedValueType
import dotty.tools.dotc.transform.Mixin
import dotty.tools.io.AbstractFile
import dotty.tools.dotc.report

Expand Down Expand Up @@ -395,12 +396,20 @@ trait BCodeHelpers extends BCodeIdiomatic {
*/
def getGenericSignature(sym: Symbol, owner: Symbol): String = {
atPhase(erasurePhase) {
val memberTpe =
def computeMemberTpe(): Type =
if (sym.is(Method)) sym.denot.info
else if sym.denot.validFor.phaseId > erasurePhase.id && sym.isField && sym.getter.exists then
// Memoization field of getter entered after erasure, see run/i17069 for an example
sym.getter.denot.info.resultType
else owner.denot.thisType.memberInfo(sym)

val memberTpe = if sym.is(MixedIn) then
mixinPhase.asInstanceOf[Mixin].mixinForwarderGenericInfos.get(sym) match
case Some(genericInfo) => genericInfo
case none => computeMemberTpe()
else
computeMemberTpe()

getGenericSignatureHelper(sym, owner, memberTpe).orNull
}
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ object Phases {
private var myErasurePhase: Phase = uninitialized
private var myElimErasedValueTypePhase: Phase = uninitialized
private var myLambdaLiftPhase: Phase = uninitialized
private var myMixinPhase: Phase = uninitialized
private var myCountOuterAccessesPhase: Phase = uninitialized
private var myFlattenPhase: Phase = uninitialized
private var myGenBCodePhase: Phase = uninitialized
Expand All @@ -264,6 +265,7 @@ object Phases {
final def gettersPhase: Phase = myGettersPhase
final def erasurePhase: Phase = myErasurePhase
final def elimErasedValueTypePhase: Phase = myElimErasedValueTypePhase
final def mixinPhase: Phase = myMixinPhase
final def lambdaLiftPhase: Phase = myLambdaLiftPhase
final def countOuterAccessesPhase = myCountOuterAccessesPhase
final def flattenPhase: Phase = myFlattenPhase
Expand Down Expand Up @@ -292,6 +294,7 @@ object Phases {
myErasurePhase = phaseOfClass(classOf[Erasure])
myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType])
myPatmatPhase = phaseOfClass(classOf[PatternMatcher])
myMixinPhase = phaseOfClass(classOf[Mixin])
myLambdaLiftPhase = phaseOfClass(classOf[LambdaLift])
myCountOuterAccessesPhase = phaseOfClass(classOf[CountOuterAccesses])
myFlattenPhase = phaseOfClass(classOf[Flatten])
Expand Down Expand Up @@ -548,6 +551,7 @@ object Phases {
def gettersPhase(using Context): Phase = ctx.base.gettersPhase
def erasurePhase(using Context): Phase = ctx.base.erasurePhase
def elimErasedValueTypePhase(using Context): Phase = ctx.base.elimErasedValueTypePhase
def mixinPhase(using Context): Phase = ctx.base.mixinPhase
def lambdaLiftPhase(using Context): Phase = ctx.base.lambdaLiftPhase
def flattenPhase(using Context): Phase = ctx.base.flattenPhase
def genBCodePhase(using Context): Phase = ctx.base.genBCodePhase
Expand Down
28 changes: 27 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import StdNames.*
import Names.*
import NameKinds.*
import NameOps.*
import Phases.erasurePhase
import ast.Trees.*

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

/** Infos before erasure of the generated mixin forwarders.
*
* These will be used to generate Java generic signatures of the mixin
* forwarders. Normally we use the types before erasure; we cannot do that
* for mixin forwarders since they are created after erasure, and therefore
* their type history does not have anything recorded for before erasure.
*/
val mixinForwarderGenericInfos = MutableSymbolMap[Type]()

override def phaseName: String = Mixin.name

override def description: String = Mixin.description
Expand Down Expand Up @@ -306,8 +316,24 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth))
yield {
util.Stats.record("mixin forwarders")
transformFollowing(DefDef(mkForwarderSym(meth.asTerm, extraFlags = MixedIn), forwarderRhsFn(meth)))
transformFollowing(DefDef(mkMixinForwarderSym(meth.asTerm), forwarderRhsFn(meth)))
}

def mkMixinForwarderSym(target: TermSymbol): TermSymbol =
val sym = mkForwarderSym(target, extraFlags = MixedIn)
val infoBeforeErasure = atPhase(erasurePhase) {
cls.thisType.memberInfo(target)
}
if !(infoBeforeErasure =:= sym.info) then
// The info before erasure would not have been the same as the info now.
// We want to store it for the backend to compute the generic Java signature.
// However, we must still avoid doing that if erasing that signature would
// not give the same erased type. If it doesn't, we'll just give a completely
// incorrect Java signature. (This could be improved by generating dedicated
// bridges, but we don't go that far; scalac doesn't either.)
if TypeErasure.transformInfo(target, infoBeforeErasure) =:= sym.info then
mixinForwarderGenericInfos(sym) = infoBeforeErasure
sym

cpy.Template(impl)(
constr =
Expand Down
1 change: 1 addition & 0 deletions tests/pos/11484/A_2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public class A_2 extends C<String> { }
6 changes: 6 additions & 0 deletions tests/pos/11484/C_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class B[A]
sealed trait T[A] {
def overloaded(that: List[T[A]]): T[A] = that.head
def overloaded(that: List[B[A]]): B[A] = that.head
}
abstract class C[A] extends T[A]
1 change: 1 addition & 0 deletions tests/pos/11512/A_2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public class A_2 extends C { }
7 changes: 7 additions & 0 deletions tests/pos/11512/C_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trait T { this: U =>
def m: Int
}
trait U {
def m: Int = ???
}
abstract class C extends U with T
4 changes: 2 additions & 2 deletions tests/run/t7932.check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
public Category C.category()
public Category C.category1()
public Category<java.lang.Object> C.category()
public Category<scala.Tuple2> C.category1()
public abstract Category<?> M2.category3()
public abstract Category<java.lang.Object> M2.category2()
public default Category<F> M1.category()
Expand Down

0 comments on commit a90225b

Please sign in to comment.