Skip to content

Commit 0617a7c

Browse files
committed
Move the trait super call encoding logic to the back-end.
1 parent a0b4441 commit 0617a7c

File tree

6 files changed

+67
-5
lines changed

6 files changed

+67
-5
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
768768
val invokeStyle =
769769
if (sym.isStaticMember) InvokeStyle.Static
770770
else if (sym.isPrivate || sym.isClassConstructor) InvokeStyle.Special
771+
else if (useInvokeSpecial(app)) InvokeStyle.Special
771772
else InvokeStyle.Virtual
772773

773774
if (invokeStyle.hasInstance) genLoadQualifier(fun)
@@ -1183,9 +1184,16 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
11831184
val isInterface = receiverClass.isEmittedInterface
11841185
import InvokeStyle._
11851186
if (style == Super) {
1186-
// DOTTY: this differ from how super-calls in traits are handled in the scalac backend,
1187-
// this is intentional but could change in the future, see https://github.com/lampepfl/dotty/issues/5928
1188-
bc.invokespecial(receiverName, jname, mdescr, isInterface)
1187+
if (isInterface && !method.isJavaDefined) {
1188+
val args = new Array[BType](bmType.argumentTypes.length + 1)
1189+
val ownerBType = toTypeKind(method.owner.info)
1190+
bmType.argumentTypes.copyToArray(args, 1)
1191+
val staticDesc = MethodBType(ownerBType :: bmType.argumentTypes, bmType.returnType).descriptor
1192+
val staticName = traitSuperAccessorName(method)
1193+
bc.invokestatic(receiverName, staticName, staticDesc, isInterface)
1194+
} else {
1195+
bc.invokespecial(receiverName, jname, mdescr, isInterface)
1196+
}
11891197
} else {
11901198
val opc = style match {
11911199
case Static => Opcodes.INVOKESTATIC

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
4747
}
4848
}
4949

50+
final def traitSuperAccessorName(sym: Symbol): String = {
51+
val nameString = sym.javaSimpleName.toString
52+
if (sym.isMixinConstructor) nameString
53+
else nameString + "$"
54+
}
55+
5056
// -----------------------------------------------------------------------------------------
5157
// finding the least upper bound in agreement with the bytecode verifier (given two internal names handed by ASM)
5258
// Background:

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,19 @@ trait BCodeSkelBuilder extends BCodeHelpers {
498498

499499
case ValDef(mods, name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()`
500500

501-
case dd @ DefDef(_, _, _, _, _, _) => genDefDef(dd.asInstanceOf[DefDef])
501+
case dd @ DefDef(_, _, _, _, _, rhs) =>
502+
val sym = dd.symbol
503+
504+
def needsStaticImplMethod: Boolean =
505+
claszSymbol.isInterface
506+
&& !rhs.isEmpty
507+
&& !sym.isPrivate
508+
&& !sym.isStaticMember
509+
510+
if needsStaticImplMethod then
511+
genStaticForwarderForDefDef(dd)
512+
513+
genDefDef(dd)
502514

503515
case Template(_, _, body) => body foreach gen
504516

@@ -535,6 +547,12 @@ trait BCodeSkelBuilder extends BCodeHelpers {
535547

536548
} // end of method initJMethod
537549

550+
def genStaticForwarderForDefDef(dd: DefDef): Unit = dd match {
551+
case DefDef(_, _, _, vparamss, _, _) =>
552+
553+
val forwarderDef = makeStaticForwarder(dd, traitSuperAccessorName(dd.symbol))
554+
genDefDef(forwarderDef)
555+
}
538556

539557
def genDefDef(dd: DefDef): Unit = dd match {
540558
case DefDef(_, _, _, vparamss, _, rhs) =>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
113113
val nme_This: Name
114114
val nme_EMPTY_PACKAGE_NAME: Name
115115
val nme_CONSTRUCTOR: Name
116+
val nme_TRAIT_CONSTRUCTOR: Name
116117
val nme_WILDCARD: Name
117118
val nme_THIS: Name
118119
val nme_PACKAGE: Name
@@ -144,6 +145,9 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
144145
def emitAnnotations(fw: asm.FieldVisitor, annotations: List[Annotation], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit
145146
def emitParamAnnotations(jmethod: asm.MethodVisitor, pannotss: List[List[Annotation]], bcodeStore: BCodeHelpers)(innerClasesStore: bcodeStore.BCInnerClassGen): Unit
146147

148+
def makeStaticForwarder(dd: DefDef, name: String): DefDef
149+
def useInvokeSpecial(apply: Apply): Boolean
150+
147151
/* means of getting class symbols from compiler */
148152
def requiredClass[T: ClassTag]: Symbol
149153
def requiredModule[T: ClassTag]: Symbol
@@ -485,6 +489,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions {
485489
def isInterface: Boolean
486490
def isGetter: Boolean
487491
def isSetter: Boolean
492+
def isMixinConstructor: Boolean = name == nme_TRAIT_CONSTRUCTOR
488493
def isGetClass: Boolean
489494
def isJavaDefined: Boolean
490495
def isDeferred: Boolean

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
9999
val nme_This: Name = StdNames.nme.This
100100
val nme_EMPTY_PACKAGE_NAME: Name = StdNames.nme.EMPTY_PACKAGE
101101
val nme_CONSTRUCTOR: Name = StdNames.nme.CONSTRUCTOR
102+
val nme_TRAIT_CONSTRUCTOR: Name = StdNames.nme.TRAIT_CONSTRUCTOR
102103
val nme_WILDCARD: Name = StdNames.nme.WILDCARD
103104
val nme_THIS: Name = StdNames.nme.THIS
104105
val nme_PACKAGE: Name = StdNames.nme.PACKAGE
@@ -365,6 +366,24 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
365366

366367
def getAnnotPickle(jclassName: String, sym: Symbol): Option[Annotation] = None
367368

369+
def makeStaticForwarder(dd: DefDef, name: String): DefDef =
370+
val origSym = dd.symbol.asTerm
371+
val info = origSym.tpe match
372+
case mt: MethodType =>
373+
MethodType(nme.SELF :: mt.paramNames, origSym.owner.typeRef :: mt.paramInfos, mt.resType)
374+
val sym = origSym.copy(
375+
name = name.toTermName,
376+
flags = Flags.Method | Flags.JavaStatic,
377+
info = info
378+
)
379+
tpd.DefDef(sym.asTerm, { paramss =>
380+
val params = paramss.head
381+
tpd.Apply(params.head.select(origSym), params.tail)
382+
.withAttachment(DottyBackendInterface.UseInvokeSpecial, ())
383+
})
384+
385+
def useInvokeSpecial(apply: Apply): Boolean =
386+
apply.hasAttachment(DottyBackendInterface.UseInvokeSpecial)
368387

369388
def getRequiredClass(fullname: String): Symbol = ctx.requiredClass(fullname)
370389

@@ -1210,4 +1229,10 @@ object DottyBackendInterface {
12101229
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
12111230
Flags.Private | Flags.Macro
12121231
}
1232+
1233+
/** An attachment on Apply nodes indicating that it should be compiled with
1234+
* `invokespecial` instead of `invokevirtual`. This is used for static
1235+
* forwarders.
1236+
*/
1237+
private val UseInvokeSpecial = new util.Property.Key[Unit]
12131238
}

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class Compiler {
110110
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
111111
new Instrumentation, // Count closure allocations under -Yinstrument-closures
112112
new GetClass) :: // Rewrites getClass calls on primitive types.
113-
List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to
113+
List(//new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to
114114
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
115115
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
116116
new ElimStaticThis, // Replace `this` references to static objects by global identifiers

0 commit comments

Comments
 (0)