Skip to content

Commit 79e24d5

Browse files
authored
Merge pull request scala#5251 from adriaanm/rebase-5177
Emit trait method bodies in statics [rebase of scala#5177]
2 parents 7a7fdac + d8c862b commit 79e24d5

39 files changed

+418
-222
lines changed

project/ScriptCommands.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object ScriptCommands {
1313
// Append build.timestamp to Artifactory URL to get consistent build numbers (see https://github.com/sbt/sbt/issues/2088):
1414
publishTo in Global := Some("scala-pr" at url.replaceAll("/$", "") + ";build.timestamp=" + System.currentTimeMillis),
1515
publishArtifact in (Compile, packageDoc) in ThisBuild := false,
16-
scalacOptions in Compile in ThisBuild += "-Yopt:l:classpath",
16+
scalacOptions in Compile in ThisBuild += "-opt:l:classpath",
1717
logLevel in ThisBuild := Level.Info,
1818
logLevel in update in ThisBuild := Level.Warn
1919
), state)

scripts/jobs/integrate/bootstrap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ buildPartest() {
247247
else
248248
update scala scala-partest "$PARTEST_REF" && gfxd
249249
doc="$(docTask $PARTEST_BUILT)"
250-
sbtBuild 'set version :="'$PARTEST_VER'"' 'set VersionKeys.scalaXmlVersion := "'$XML_VER'"' 'set VersionKeys.scalaCheckVersion := "'$SCALACHECK_VER'"' $clean "$doc" test "${buildTasks[@]}"
250+
sbtBuild 'set version :="'$PARTEST_VER'"' 'set VersionKeys.scalaXmlVersion := "'$XML_VER'"' $clean "$doc" test "${buildTasks[@]}"
251251
PARTEST_BUILT="yes"
252252
fi
253253
}
@@ -282,7 +282,7 @@ buildModules() {
282282
buildXML
283283
buildParsers
284284
buildSwing
285-
buildScalacheck
285+
# buildScalacheck
286286
buildPartest
287287
}
288288

@@ -424,7 +424,7 @@ deriveModuleVersions() {
424424
echo "Module versions (versioning strategy: $moduleVersioning):"
425425
echo "PARSERS = $PARSERS_VER at $PARSERS_REF"
426426
echo "PARTEST = $PARTEST_VER at $PARTEST_REF"
427-
echo "SCALACHECK = $SCALACHECK_VER at $SCALACHECK_REF"
427+
# echo "SCALACHECK = $SCALACHECK_VER at $SCALACHECK_REF"
428428
echo "SWING = $SWING_VER at $SWING_REF"
429429
echo "XML = $XML_VER at $XML_REF"
430430

@@ -444,7 +444,7 @@ removeExistingBuilds() {
444444
local storageApiUrl=`echo $releaseTempRepoUrl | sed 's/\(scala-release-temp\)/api\/storage\/\1/'`
445445
local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri' | grep -v actors-migration`
446446

447-
for module in "org/scalacheck" $scalaLangModules; do
447+
for module in $scalaLangModules; do
448448
local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | contains(\"$SCALA_VER\")) | .uri"`
449449
for artifact in $artifacts; do
450450
echo "Deleting $releaseTempRepoUrl$module$artifact"
@@ -464,7 +464,7 @@ constructUpdatedModuleVersions() {
464464
updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscala-xml.version.number=$XML_VER")
465465

466466
updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dpartest.version.number=$PARTEST_VER")
467-
updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscalacheck.version.number=$SCALACHECK_VER")
467+
# updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscalacheck.version.number=$SCALACHECK_VER")
468468

469469
# allow overriding the jline version using a jenkins build parameter
470470
if [ ! -z "$JLINE_VER" ] ; then updatedModuleVersions=("${updatedModuleVersions[@]}" "-Djline.version=$JLINE_VER"); fi

src/compiler/scala/tools/nsc/ast/TreeGen.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,13 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
336336
* - are associating the RHS with a cloned symbol, but intend for the original
337337
* method to remain and for recursive calls to target it.
338338
*/
339-
final def mkStatic(orig: DefDef, maybeClone: Symbol => Symbol): DefDef = {
339+
final def mkStatic(orig: DefDef, newName: Name, maybeClone: Symbol => Symbol): DefDef = {
340340
assert(phase.erasedTypes, phase)
341341
assert(!orig.symbol.hasFlag(SYNCHRONIZED), orig.symbol.defString)
342342
val origSym = orig.symbol
343343
val origParams = orig.symbol.info.params
344344
val newSym = maybeClone(orig.symbol)
345+
newSym.setName(newName)
345346
newSym.setFlag(STATIC)
346347
// Add an explicit self parameter
347348
val selfParamSym = newSym.newSyntheticValueParam(newSym.owner.typeConstructor, nme.SELF).setFlag(ARTIFACT)

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ package jvm
1111

1212
import scala.annotation.switch
1313
import scala.reflect.internal.Flags
14-
1514
import scala.tools.asm
1615
import GenBCode._
1716
import BackendReporting._
17+
import scala.tools.asm.Opcodes
1818
import scala.tools.asm.tree.MethodInsnNode
1919
import scala.tools.nsc.backend.jvm.BCodeHelpers.{InvokeStyle, TestOp}
2020

@@ -637,15 +637,15 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
637637
val nativeKind = tpeTK(expr)
638638
genLoad(expr, nativeKind)
639639
val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind)
640-
bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
640+
bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, itf = false, app.pos)
641641
generatedType = boxResultType(fun.symbol)
642642

643643
case Apply(fun, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
644644
genLoad(expr)
645645
val boxType = unboxResultType(fun.symbol)
646646
generatedType = boxType
647647
val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType)
648-
bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
648+
bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, itf = false, app.pos)
649649

650650
case app @ Apply(fun, args) =>
651651
val sym = fun.symbol
@@ -1058,31 +1058,40 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
10581058
}
10591059

10601060
receiverClass.info // ensure types the type is up to date; erasure may add lateINTERFACE to traits
1061-
val receiverName = internalName(receiverClass)
1062-
1063-
// super calls are only allowed to direct parents
1064-
if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) {
1065-
thisBType.info.get.inlineInfo.lateInterfaces += receiverName
1066-
cnode.interfaces.add(receiverName)
1067-
}
1061+
val receiverBType = classBTypeFromSymbol(receiverClass)
1062+
val receiverName = receiverBType.internalName
10681063

10691064
def needsInterfaceCall(sym: Symbol) = {
10701065
sym.isTraitOrInterface ||
10711066
sym.isJavaDefined && sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass)
10721067
}
10731068

1074-
val jname = method.javaSimpleName.toString
1075-
val bmType = methodBTypeFromSymbol(method)
1076-
val mdescr = bmType.descriptor
1069+
val jname = method.javaSimpleName.toString
1070+
val bmType = methodBTypeFromSymbol(method)
1071+
val mdescr = bmType.descriptor
10771072

1073+
val isInterface = receiverBType.isInterface.get
10781074
import InvokeStyle._
1079-
style match {
1080-
case Static => bc.invokestatic (receiverName, jname, mdescr, pos)
1081-
case Special => bc.invokespecial (receiverName, jname, mdescr, pos)
1082-
case Virtual =>
1083-
if (needsInterfaceCall(receiverClass)) bc.invokeinterface(receiverName, jname, mdescr, pos)
1084-
else bc.invokevirtual (receiverName, jname, mdescr, pos)
1085-
case Super => bc.invokespecial (receiverName, jname, mdescr, pos)
1075+
if (style == Super) {
1076+
assert(receiverClass == methodOwner, s"for super call, expecting $receiverClass == $methodOwner")
1077+
if (receiverClass.isTrait && !receiverClass.isJavaDefined) {
1078+
val staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor
1079+
val staticName = traitImplMethodName(method).toString
1080+
bc.invokestatic(receiverName, staticName, staticDesc, isInterface, pos)
1081+
} else {
1082+
if (receiverClass.isTraitOrInterface) {
1083+
// An earlier check in Mixin reports an error in this case, so it doesn't reach the backend
1084+
assert(cnode.interfaces.contains(receiverName), s"cannot invokespecial $receiverName.$jname, the interface is not a direct parent.")
1085+
}
1086+
bc.invokespecial(receiverName, jname, mdescr, isInterface, pos)
1087+
}
1088+
} else {
1089+
val opc = style match {
1090+
case Static => Opcodes.INVOKESTATIC
1091+
case Special => Opcodes.INVOKESPECIAL
1092+
case Virtual => if (isInterface) Opcodes.INVOKEINTERFACE else Opcodes.INVOKEVIRTUAL
1093+
}
1094+
bc.emitInvoke(opc, receiverName, jname, mdescr, isInterface, pos)
10861095
}
10871096

10881097
bmType.returnType

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

Lines changed: 10 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import scala.tools.asm
1111
import scala.tools.nsc.io.AbstractFile
1212
import GenBCode._
1313
import BackendReporting._
14+
import scala.reflect.internal.Flags
1415

1516
/*
1617
* Traits encapsulating functionality to convert Scala AST Trees into ASM ClassNodes.
@@ -49,6 +50,14 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
4950
}
5051
}
5152

53+
def needsStaticImplMethod(sym: Symbol) = sym.hasAttachment[global.mixer.NeedStaticImpl.type]
54+
55+
final def traitImplMethodName(sym: Symbol): Name = {
56+
val name = sym.javaSimpleName
57+
if (sym.isMixinConstructor) name
58+
else name.append(nme.NAME_JOIN_STRING)
59+
}
60+
5261
/**
5362
* True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
5463
* member class. This method is used to decide if we should emit an EnclosingMethod attribute.
@@ -230,58 +239,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
230239
sym.isErroneous
231240
}
232241

233-
/**
234-
* Build the [[InlineInfo]] for a class symbol.
235-
*/
236-
def buildInlineInfoFromClassSymbol(classSym: Symbol, classSymToInternalName: Symbol => InternalName, methodSymToDescriptor: Symbol => String): InlineInfo = {
237-
val isEffectivelyFinal = classSym.isEffectivelyFinal
238-
239-
val sam = {
240-
if (classSym.isEffectivelyFinal) None
241-
else {
242-
// Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
243-
// empty parameter list in later phases and would therefore be picked as SAM.
244-
val samSym = exitingPickler(definitions.samOf(classSym.tpe))
245-
if (samSym == NoSymbol) None
246-
else Some(samSym.javaSimpleName.toString + methodSymToDescriptor(samSym))
247-
}
248-
}
249-
250-
var warning = Option.empty[ClassSymbolInfoFailureSI9111]
251-
252-
// Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
253-
// primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
254-
val methodInlineInfos = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({
255-
case methodSym =>
256-
if (completeSilentlyAndCheckErroneous(methodSym)) {
257-
// Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
258-
if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
259-
warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
260-
None
261-
} else {
262-
val name = methodSym.javaSimpleName.toString // same as in genDefDef
263-
val signature = name + methodSymToDescriptor(methodSym)
264-
265-
// In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
266-
// method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
267-
// so they are not marked final in the InlineInfo attribute.
268-
//
269-
// However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
270-
// work, the abstract accessor for O will be marked effectivelyFinal.
271-
val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred
272-
273-
val info = MethodInlineInfo(
274-
effectivelyFinal = effectivelyFinal,
275-
annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
276-
annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)
277-
)
278-
Some((signature, info))
279-
}
280-
}).toMap
281-
282-
InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
283-
}
284-
285242
/*
286243
* must-single-thread
287244
*/
@@ -568,15 +525,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
568525
/**
569526
* The class internal name for a given class symbol.
570527
*/
571-
final def internalName(sym: Symbol): String = {
572-
// For each java class, the scala compiler creates a class and a module (thus a module class).
573-
// If the `sym` is a java module class, we use the java class instead. This ensures that the
574-
// ClassBType is created from the main class (instead of the module class).
575-
// The two symbols have the same name, so the resulting internalName is the same.
576-
// Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
577-
val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
578-
classBTypeFromSymbol(classSym).internalName
579-
}
528+
final def internalName(sym: Symbol): String = classBTypeFromSymbol(sym).internalName
580529
} // end of trait BCInnerClassGen
581530

582531
trait BCAnnotGen extends BCInnerClassGen {

src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ abstract class BCodeIdiomatic extends SubComponent {
190190
JavaStringBuilderClassName,
191191
INSTANCE_CONSTRUCTOR_NAME,
192192
"()V",
193+
itf = false,
193194
pos
194195
)
195196
}
@@ -373,30 +374,27 @@ abstract class BCodeIdiomatic extends SubComponent {
373374
final def rem(tk: BType) { emitPrimitive(JCodeMethodN.remOpcodes, tk) } // can-multi-thread
374375

375376
// can-multi-thread
376-
final def invokespecial(owner: String, name: String, desc: String, pos: Position) {
377-
addInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, false, pos)
377+
final def invokespecial(owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
378+
emitInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, itf, pos)
378379
}
379380
// can-multi-thread
380-
final def invokestatic(owner: String, name: String, desc: String, pos: Position) {
381-
addInvoke(Opcodes.INVOKESTATIC, owner, name, desc, false, pos)
381+
final def invokestatic(owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
382+
emitInvoke(Opcodes.INVOKESTATIC, owner, name, desc, itf, pos)
382383
}
383384
// can-multi-thread
384-
final def invokeinterface(owner: String, name: String, desc: String, pos: Position) {
385-
addInvoke(Opcodes.INVOKEINTERFACE, owner, name, desc, true, pos)
385+
final def invokeinterface(owner: String, name: String, desc: String, pos: Position): Unit = {
386+
emitInvoke(Opcodes.INVOKEINTERFACE, owner, name, desc, itf = true, pos)
386387
}
387388
// can-multi-thread
388-
final def invokevirtual(owner: String, name: String, desc: String, pos: Position) {
389-
addInvoke(Opcodes.INVOKEVIRTUAL, owner, name, desc, false, pos)
389+
final def invokevirtual(owner: String, name: String, desc: String, pos: Position): Unit = {
390+
emitInvoke(Opcodes.INVOKEVIRTUAL, owner, name, desc, itf = false, pos)
390391
}
391392

392-
private def addInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean, pos: Position) = {
393+
def emitInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
393394
val node = new MethodInsnNode(opcode, owner, name, desc, itf)
394395
jmethod.instructions.add(node)
395396
if (settings.optInlinerEnabled) callsitePositions(node) = pos
396397
}
397-
final def invokedynamic(owner: String, name: String, desc: String) {
398-
jmethod.visitMethodInsn(Opcodes.INVOKEDYNAMIC, owner, name, desc)
399-
}
400398

401399
// can-multi-thread
402400
final def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) }

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,22 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
488488

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

491-
case dd : DefDef => genDefDef(dd)
491+
case dd : DefDef =>
492+
val sym = dd.symbol
493+
if (needsStaticImplMethod(sym)) {
494+
val staticDefDef = global.gen.mkStatic(dd, traitImplMethodName(sym), _.cloneSymbol)
495+
val forwarderDefDef = {
496+
val forwarderBody = Apply(global.gen.mkAttributedRef(staticDefDef.symbol), This(sym.owner).setType(sym.owner.typeConstructor) :: dd.vparamss.head.map(p => global.gen.mkAttributedIdent(p.symbol))).setType(sym.info.resultType)
497+
// we don't want to the optimizer to inline the static method into the forwarder. Instead,
498+
// the backend has a special case to transitively inline into a callsite of the forwarder
499+
// when the forwarder itself is inlined.
500+
forwarderBody.updateAttachment(NoInlineCallsiteAttachment)
501+
deriveDefDef(dd)(_ => global.atPos(dd.pos)(forwarderBody))
502+
}
503+
genDefDef(staticDefDef)
504+
if (!sym.isMixinConstructor)
505+
genDefDef(forwarderDefDef)
506+
} else genDefDef(dd)
492507

493508
case Template(_, _, body) => body foreach gen
494509

src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,7 @@ abstract class BTypes {
225225

226226
val inlineInfo = inlineInfoFromClassfile(classNode)
227227

228-
val classfileInterfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
229-
val interfaces = classfileInterfaces.filterNot(i => inlineInfo.lateInterfaces.contains(i.internalName))
228+
val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
230229

231230
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
232231
classBType
@@ -1147,25 +1146,6 @@ object BTypes {
11471146
sam: Option[String],
11481147
methodInfos: Map[String, MethodInlineInfo],
11491148
warning: Option[ClassInlineInfoWarning]) {
1150-
/**
1151-
* A super call (invokespecial) to a default method T.m is only allowed if the interface T is
1152-
* a direct parent of the class. Super calls are introduced for example in Mixin when generating
1153-
* forwarder methods:
1154-
*
1155-
* trait T { override def clone(): Object = "hi" }
1156-
* trait U extends T
1157-
* class C extends U
1158-
*
1159-
* The class C gets a forwarder that invokes T.clone(). During code generation the interface T
1160-
* is added as direct parent to class C. Note that T is not a (direct) parent in the frontend
1161-
* type of class C.
1162-
*
1163-
* All interfaces that are added to a class during code generation are added to this buffer and
1164-
* stored in the InlineInfo classfile attribute. This ensures that the ClassBTypes for a
1165-
* specific class is the same no matter if it's constructed from a Symbol or from a classfile.
1166-
* This is tested in BTypesFromClassfileTest.
1167-
*/
1168-
val lateInterfaces: ListBuffer[InternalName] = ListBuffer.empty
11691149
}
11701150

11711151
val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None)

0 commit comments

Comments
 (0)